@nakedcreativity/cee-ms-node 2.0.0-beta.5 → 2.0.0-beta.7

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.
@@ -3,6 +3,7 @@ import type express from 'express';
3
3
  import type { Context, FilterImplOptions } from 'liquidjs';
4
4
  export declare enum CacheSource {
5
5
  Local = 0,
6
+ /** The bucket, read with the host's own AWS credentials (`cacheBucket`, `cacheRegion`). */
6
7
  S3 = 1
7
8
  }
8
9
  export interface CeemsConfig {
@@ -18,9 +19,11 @@ export interface CeemsConfig {
18
19
  cacheRegion?: string;
19
20
  cacheFolder?: string;
20
21
  /**
21
- * Where Cee.ms 2 publishes in the bucket or folder: `ceems/` by default, so it can publish to the same bucket
22
- * as Cee.ms 1 (which writes `pages/` and `data/` at the top) while a site moves over. `''` reads from the top.
22
+ * The environment this renderer serves: published files are read from `<environment>/` in the bucket or
23
+ * folder. Defaults to the `CEEMS_ENVIRONMENT` environment variable, then `development`.
23
24
  */
25
+ environment?: string;
26
+ /** Where published files are read from in the bucket or folder: `<environment>/` unless set. */
24
27
  cachePrefix?: string;
25
28
  whitelistedCookies?: Array<string>;
26
29
  tags?: Array<LiquidTag>;
@@ -76,6 +79,83 @@ export interface CeemsSite {
76
79
  */
77
80
  settings?: Record<string, any>;
78
81
  collections?: any;
82
+ /**
83
+ * Every published page by id (absent for sites last published before it): page links read their address from here when the page
84
+ * renders, so a renamed or moved page is right everywhere once it is published. A link to a page that
85
+ * isn't here is broken.
86
+ */
87
+ pageIndex?: Record<string, CeemsPageIndexEntry>;
88
+ /** The documents published content uses, by id, read by document links and values as the page renders. */
89
+ documents?: Record<string, CeemsPublishedDocument>;
90
+ /** The site's People by row id, for bylines, profile pages and row fields. */
91
+ people?: Record<string, CeemsPerson>;
92
+ }
93
+ /** A person from the site's People, in `site.json` `people`. Sites may add columns of their own. */
94
+ export interface CeemsPerson {
95
+ name: string;
96
+ jobTitle?: string;
97
+ photo?: CeemsImage;
98
+ /** Rich text. */
99
+ bio?: string;
100
+ links?: {
101
+ url: string;
102
+ }[];
103
+ /** `links` as addresses, for structured data. */
104
+ sameAs: string[];
105
+ email?: string;
106
+ telephone?: string;
107
+ /** Their profile page, if they have one. */
108
+ profile?: {
109
+ kind: 'page';
110
+ pageId: string;
111
+ url: string;
112
+ };
113
+ [column: string]: unknown;
114
+ }
115
+ /** A byline author, as published: a person from People, someone typed in, or the organisation. */
116
+ export type CeemsBylineAuthor = {
117
+ kind: 'person';
118
+ collectionId: string;
119
+ rowId: string;
120
+ } | {
121
+ kind: 'guest';
122
+ name: string;
123
+ url?: string;
124
+ jobTitle?: string;
125
+ } | {
126
+ kind: 'organisation';
127
+ };
128
+ /** A published page, in `site.json` `pageIndex`. */
129
+ export interface CeemsPageIndexEntry {
130
+ /** Its address, e.g. `/about/team`, or `/` for the home page. */
131
+ path: string;
132
+ name: string;
133
+ }
134
+ export interface CeemsDocumentInfo {
135
+ numpages?: number;
136
+ dimensions?: {
137
+ width: number;
138
+ height: number;
139
+ };
140
+ }
141
+ /** A library document, in `site.json` `documents`. */
142
+ export interface CeemsPublishedDocument {
143
+ url: string;
144
+ thumbnail?: string;
145
+ info?: CeemsDocumentInfo;
146
+ }
147
+ /** A published `data/<id>.json`. */
148
+ export interface CeemsDataFile {
149
+ _id: string;
150
+ name: string;
151
+ slug: string;
152
+ columns: any[];
153
+ /** Each row's values, in order: as Cee.ms 1 published them. */
154
+ data: Record<string, any>[];
155
+ /** The same rows with their ids, for row references. Absent for data last published before it. */
156
+ rows?: ({
157
+ _id: string;
158
+ } & Record<string, any>)[];
79
159
  }
80
160
  /** The published `site.json` `site`. */
81
161
  export interface CeemsSiteInfo {
@@ -90,6 +170,10 @@ export interface CeemsSiteInfo {
90
170
  faqModules?: Record<string, FaqModuleMapping>;
91
171
  /** For module types holding reviews, which attach to a product or event page. */
92
172
  reviewModules?: Record<string, ReviewsModuleMapping>;
173
+ /** For module types featuring people: their row field to People, by module config type. */
174
+ peopleModules?: Record<string, {
175
+ field: string;
176
+ }>;
93
177
  };
94
178
  [key: string]: any;
95
179
  }
@@ -100,7 +184,7 @@ export type StructuredDataType = 'WEBSITE' | 'WEBPAGE' | 'BREADCRUMB' | 'ORGANIS
100
184
  * `HOME` is the site's front page, which by default carries the site and organisation. Templates and modules
101
185
  * can read it, e.g. `{% if page.entity == 'PRODUCT' %}`.
102
186
  */
103
- export type PageEntity = 'HOME' | 'PAGE' | 'PRODUCT' | 'EVENT' | 'PERSON' | 'SERVICE';
187
+ export type PageEntity = 'HOME' | 'PAGE' | 'ARTICLE' | 'PRODUCT' | 'EVENT' | 'PERSON' | 'SERVICE';
104
188
  /** The types each kind of page outputs unless it chooses its own. */
105
189
  export interface StructuredDataDefaults {
106
190
  home: StructuredDataType[];
@@ -244,12 +328,23 @@ export interface CeemsPage {
244
328
  type?: 'blog' | 'article';
245
329
  /** Published from Cee.ms with the default resolved: `HOME` for the home page, else `PAGE`. */
246
330
  entity?: PageEntity;
331
+ /** The site's own page type, such as `property`, by key. Its `entity` is the type's base. */
332
+ pageType?: string;
247
333
  tags?: string[];
248
334
  /** An article's date. */
249
335
  date?: string;
250
336
  publishedAt?: string;
251
337
  updatedAt?: string;
252
338
  structuredData?: CeemsPageStructuredData;
339
+ /** An article's byline, as published. Templates read `page.authors`. */
340
+ byline?: {
341
+ authors: CeemsBylineAuthor[];
342
+ reviewedBy?: unknown;
343
+ };
344
+ /** For entity `PERSON`: the person whose profile page it is, as a `RowDrop` once loaded. */
345
+ person?: unknown;
346
+ /** The byline, resolved (see `bylineAuthors`): people, guests and the organisation, in order. */
347
+ authors?: Record<string, unknown>[];
253
348
  [key: string]: any;
254
349
  }
255
350
  export interface CeemsRegion {
package/lib/interfaces.js CHANGED
@@ -4,6 +4,7 @@ exports.CacheSource = void 0;
4
4
  var CacheSource;
5
5
  (function (CacheSource) {
6
6
  CacheSource[CacheSource["Local"] = 0] = "Local";
7
+ /** The bucket, read with the host's own AWS credentials (`cacheBucket`, `cacheRegion`). */
7
8
  CacheSource[CacheSource["S3"] = 1] = "S3";
8
9
  })(CacheSource || (exports.CacheSource = CacheSource = {}));
9
10
  //# sourceMappingURL=interfaces.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":";;;AAIA,IAAY,WAGX;AAHD,WAAY,WAAW;IACnB,+CAAK,CAAA;IACL,yCAAE,CAAA;AACN,CAAC,EAHW,WAAW,2BAAX,WAAW,QAGtB"}
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":";;;AAIA,IAAY,WAIX;AAJD,WAAY,WAAW;IACnB,+CAAK,CAAA;IACL,2FAA2F;IAC3F,yCAAE,CAAA;AACN,CAAC,EAJW,WAAW,2BAAX,WAAW,QAItB"}
package/lib/links.d.ts CHANGED
@@ -1,9 +1,24 @@
1
1
  import { Drop } from 'liquidjs';
2
+ import type { CeemsDataFile, CeemsDocumentInfo, CeemsPageIndexEntry, CeemsPublishedDocument, CeemsSite } from './interfaces';
3
+ /**
4
+ * What references resolve against when a page renders: the published pages and documents from `site.json`,
5
+ * and the rows of the data collections the page points at. Each is published once, so a renamed page or an
6
+ * edited row is right everywhere as soon as that one change is published. A site published before Cee.ms
7
+ * sent these has none of them, and links read their stored address as before.
8
+ */
9
+ export interface LinkContext {
10
+ pageIndex?: Record<string, CeemsPageIndexEntry>;
11
+ documents?: Record<string, CeemsPublishedDocument>;
12
+ /** A row by collection and row id, or undefined if it isn't published (or was deleted). */
13
+ row?: (collectionId: string, rowId: string) => Record<string, unknown> | undefined;
14
+ }
2
15
  /**
3
16
  * A stored link value in templates. It reads as its address, so a field that held a URL string and became a
4
17
  * link field still works where templates print it (`{{ item.url }}`), filter it (`| escape`, `| append`),
5
18
  * compare it (`== '/about'`, `!= blank`) or measure it (`| size`); and its parts are there too
6
19
  * (`{{ item.url.kind }}`, `.pageId`, `.anchor`). `| json` shows the whole link.
20
+ *
21
+ * A page link whose page isn't published reads as `''`, with `broken` true, so `{% if link %}` leaves it out.
7
22
  */
8
23
  export declare class LinkDrop extends Drop {
9
24
  kind: string;
@@ -12,6 +27,7 @@ export declare class LinkDrop extends Drop {
12
27
  documentId?: string;
13
28
  anchor?: string;
14
29
  email?: string;
30
+ broken?: boolean;
15
31
  constructor(link: Record<string, unknown>);
16
32
  valueOf(): string;
17
33
  toString(): string;
@@ -21,5 +37,54 @@ export declare class LinkDrop extends Drop {
21
37
  get size(): number;
22
38
  toJSON(): Record<string, unknown>;
23
39
  }
24
- /** Wraps every stored link in a published page, data file or draft in a `LinkDrop`, in place. */
25
- export declare function wrapLinks<T>(value: T): T;
40
+ /**
41
+ * A document value (`{ documentId, url, thumbnail, info }`) in templates, read from the published documents
42
+ * so a replaced file is right everywhere. It prints as its address, so `{{ download }}` and
43
+ * `{{ download.url }}` both work.
44
+ */
45
+ export declare class DocumentDrop extends Drop {
46
+ documentId?: string;
47
+ url: string;
48
+ thumbnail?: string;
49
+ info?: CeemsDocumentInfo;
50
+ constructor(value: Record<string, unknown>);
51
+ valueOf(): string;
52
+ toString(): string;
53
+ toJSON(): Record<string, unknown>;
54
+ }
55
+ /**
56
+ * A row of a data collection, from a `row` field: its columns by key (`{{ author.name }}`,
57
+ * `{% image person.photo 'square' %}`), plus `_id`. It prints as its `name`, else its first text value.
58
+ */
59
+ export declare class RowDrop extends Drop {
60
+ _id: string;
61
+ [column: string]: unknown;
62
+ constructor(row: Record<string, unknown>);
63
+ toString(): string;
64
+ valueOf(): string;
65
+ toJSON(): Record<string, unknown>;
66
+ }
67
+ /**
68
+ * Rich text links carry the page they point at (`data-page-id`); their `href` comes from the published page,
69
+ * keeping any `#anchor`, and is dropped when that page isn't published. Documents' come from `documents`.
70
+ */
71
+ export declare function resolveRichText(html: string, context: LinkContext): string;
72
+ /**
73
+ * Wraps every stored link in a published page, data file or draft in a `LinkDrop`, every document value in a
74
+ * `DocumentDrop` and every row reference in a `RowDrop`, in place, resolving each against `context`. A row
75
+ * that isn't published reads as nothing, and is left out of lists.
76
+ */
77
+ export declare function wrapLinks<T>(value: T, context?: LinkContext, depth?: number): T;
78
+ /** The rows a page's row references and byline authors point at, by collection id. */
79
+ export declare function referencedRows(value: unknown, found?: Map<string, Set<string>>): Map<string, Set<string>>;
80
+ /**
81
+ * The context for a site and the data files its page points at. People come from `site.json` `people` (with
82
+ * `sameAs`), so a person reads the same wherever they are referenced.
83
+ */
84
+ export declare function linkContextFor(site: CeemsSite | undefined, files?: Iterable<CeemsDataFile | null>): LinkContext;
85
+ /**
86
+ * An article's byline as templates and structured data read it, in order: `page.authors`. A person is their
87
+ * People row (`{{ author.name }}`, `{{ author.profile }}`); a guest is what was typed; the organisation is the
88
+ * site's. Each has `kind`, `name` and `url` (a person's profile page, else their first profile link).
89
+ */
90
+ export declare function bylineAuthors(page: Record<string, any>, site: CeemsSite | undefined, context: LinkContext): Record<string, unknown>[];
package/lib/links.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LinkDrop = void 0;
3
+ exports.RowDrop = exports.DocumentDrop = exports.LinkDrop = void 0;
4
+ exports.resolveRichText = resolveRichText;
4
5
  exports.wrapLinks = wrapLinks;
6
+ exports.referencedRows = referencedRows;
7
+ exports.linkContextFor = linkContextFor;
8
+ exports.bylineAuthors = bylineAuthors;
5
9
  const liquidjs_1 = require("liquidjs");
6
10
  /** The kinds of link Cee.ms stores: `{ kind, url, pageId?, documentId?, anchor?, email? }`. */
7
11
  const LINK_KINDS = ['url', 'page', 'document', 'anchor', 'email'];
@@ -10,6 +14,8 @@ const LINK_KINDS = ['url', 'page', 'document', 'anchor', 'email'];
10
14
  * link field still works where templates print it (`{{ item.url }}`), filter it (`| escape`, `| append`),
11
15
  * compare it (`== '/about'`, `!= blank`) or measure it (`| size`); and its parts are there too
12
16
  * (`{{ item.url.kind }}`, `.pageId`, `.anchor`). `| json` shows the whole link.
17
+ *
18
+ * A page link whose page isn't published reads as `''`, with `broken` true, so `{% if link %}` leaves it out.
13
19
  */
14
20
  class LinkDrop extends liquidjs_1.Drop {
15
21
  kind;
@@ -18,6 +24,7 @@ class LinkDrop extends liquidjs_1.Drop {
18
24
  documentId;
19
25
  anchor;
20
26
  email;
27
+ broken;
21
28
  constructor(link) {
22
29
  super();
23
30
  Object.assign(this, link);
@@ -41,21 +48,208 @@ class LinkDrop extends liquidjs_1.Drop {
41
48
  }
42
49
  }
43
50
  exports.LinkDrop = LinkDrop;
51
+ /**
52
+ * A document value (`{ documentId, url, thumbnail, info }`) in templates, read from the published documents
53
+ * so a replaced file is right everywhere. It prints as its address, so `{{ download }}` and
54
+ * `{{ download.url }}` both work.
55
+ */
56
+ class DocumentDrop extends liquidjs_1.Drop {
57
+ documentId;
58
+ url;
59
+ thumbnail;
60
+ info;
61
+ constructor(value) {
62
+ super();
63
+ Object.assign(this, value);
64
+ }
65
+ valueOf() {
66
+ return this.url;
67
+ }
68
+ toString() {
69
+ return this.url;
70
+ }
71
+ toJSON() {
72
+ return { ...this };
73
+ }
74
+ }
75
+ exports.DocumentDrop = DocumentDrop;
76
+ /**
77
+ * A row of a data collection, from a `row` field: its columns by key (`{{ author.name }}`,
78
+ * `{% image person.photo 'square' %}`), plus `_id`. It prints as its `name`, else its first text value.
79
+ */
80
+ class RowDrop extends liquidjs_1.Drop {
81
+ _id;
82
+ constructor(row) {
83
+ super();
84
+ Object.assign(this, row);
85
+ }
86
+ toString() {
87
+ if (typeof this.name === 'string')
88
+ return this.name;
89
+ const first = Object.entries(this).find(([key, v]) => key !== '_id' && typeof v === 'string');
90
+ return first ? String(first[1]) : '';
91
+ }
92
+ valueOf() {
93
+ return this.toString();
94
+ }
95
+ toJSON() {
96
+ return { ...this };
97
+ }
98
+ }
99
+ exports.RowDrop = RowDrop;
44
100
  const isLink = (value) => typeof value.kind === 'string' && LINK_KINDS.includes(value.kind) && typeof value.url === 'string';
45
- /** Wraps every stored link in a published page, data file or draft in a `LinkDrop`, in place. */
46
- function wrapLinks(value) {
101
+ const isRowRef = (value) => value.kind === 'row' && value.rowId !== undefined && value.collectionId !== undefined;
102
+ const isDocumentValue = (value) => value.documentId !== undefined && typeof value.url === 'string' && value.kind === undefined;
103
+ const RICH_LINK = /<a\b([^>]*)>/gi;
104
+ /**
105
+ * Rich text links carry the page they point at (`data-page-id`); their `href` comes from the published page,
106
+ * keeping any `#anchor`, and is dropped when that page isn't published. Documents' come from `documents`.
107
+ */
108
+ function resolveRichText(html, context) {
109
+ if (!context.pageIndex && !context.documents)
110
+ return html;
111
+ return html.replace(RICH_LINK, (tag, attrs) => {
112
+ const pageId = /\bdata-page-id="([0-9a-f]{24})"/i.exec(attrs)?.[1];
113
+ const documentId = /\bdata-document-id="([0-9a-f]{24})"/i.exec(attrs)?.[1];
114
+ let href;
115
+ if (pageId && context.pageIndex) {
116
+ const anchor = /\bhref="[^"#]*#([^"]*)"/i.exec(attrs)?.[1];
117
+ const entry = context.pageIndex[pageId];
118
+ href = entry ? entry.path + (anchor ? `#${anchor}` : '') : null;
119
+ }
120
+ else if (documentId && context.documents) {
121
+ href = context.documents[documentId]?.url;
122
+ }
123
+ if (href === undefined)
124
+ return tag;
125
+ if (href === null)
126
+ return tag.replace(/\s*\bhref="[^"]*"/i, '');
127
+ const value = `href="${href.replace(/&/g, '&amp;').replace(/"/g, '&quot;')}"`;
128
+ return /\bhref="[^"]*"/i.test(attrs) ? tag.replace(/\bhref="[^"]*"/i, value) : `<a ${value}${attrs}>`;
129
+ });
130
+ }
131
+ function linkDrop(record, context) {
132
+ if (record.kind === 'page' && record.pageId && context.pageIndex) {
133
+ const entry = context.pageIndex[String(record.pageId)];
134
+ const anchor = record.anchor ? `#${record.anchor}` : '';
135
+ return new LinkDrop(entry ? { ...record, url: entry.path + anchor, broken: false } : { ...record, url: '', broken: true });
136
+ }
137
+ if (record.kind === 'document' && record.documentId && context.documents) {
138
+ const document = context.documents[String(record.documentId)];
139
+ if (document)
140
+ return new LinkDrop({ ...record, url: document.url });
141
+ }
142
+ return new LinkDrop(record);
143
+ }
144
+ /** Rows inside rows resolve this deep, so rows that point at each other can't loop. */
145
+ const ROW_DEPTH = 2;
146
+ /**
147
+ * Wraps every stored link in a published page, data file or draft in a `LinkDrop`, every document value in a
148
+ * `DocumentDrop` and every row reference in a `RowDrop`, in place, resolving each against `context`. A row
149
+ * that isn't published reads as nothing, and is left out of lists.
150
+ */
151
+ function wrapLinks(value, context = {}, depth = 0) {
47
152
  if (Array.isArray(value)) {
48
- for (let i = 0; i < value.length; i++)
49
- value[i] = wrapLinks(value[i]);
153
+ const wrapped = value.map((item) => wrapLinks(item, context, depth));
154
+ // a list of rows skips the ones that are gone
155
+ const kept = wrapped.filter((item, i) => item !== undefined || !isRowLike(value[i]));
156
+ value.splice(0, value.length, ...kept);
50
157
  return value;
51
158
  }
159
+ if (typeof value === 'string')
160
+ return (value.includes('<a') ? resolveRichText(value, context) : value);
52
161
  if (!value || typeof value !== 'object' || value instanceof liquidjs_1.Drop)
53
162
  return value;
54
163
  const record = value;
55
164
  if (isLink(record))
56
- return new LinkDrop(record);
165
+ return linkDrop(record, context);
166
+ if (isRowRef(record)) {
167
+ if (!context.row || depth >= ROW_DEPTH)
168
+ return (context.row ? undefined : value);
169
+ const row = context.row(String(record.collectionId), String(record.rowId));
170
+ // a copy: rows come from data files cached across renders
171
+ return (row ? new RowDrop(wrapLinks(structuredClone(row), context, depth + 1)) : undefined);
172
+ }
173
+ if (isDocumentValue(record) && context.documents) {
174
+ const document = context.documents[String(record.documentId)];
175
+ return new DocumentDrop(document ? { ...record, ...document } : record);
176
+ }
57
177
  for (const key of Object.keys(record))
58
- record[key] = wrapLinks(record[key]);
178
+ record[key] = wrapLinks(record[key], context, depth);
59
179
  return value;
60
180
  }
181
+ const isRowLike = (value) => !!value && typeof value === 'object' && isRowRef(value);
182
+ /** The rows a page's row references and byline authors point at, by collection id. */
183
+ function referencedRows(value, found = new Map()) {
184
+ if (Array.isArray(value)) {
185
+ for (const item of value)
186
+ referencedRows(item, found);
187
+ }
188
+ else if (value && typeof value === 'object' && !(value instanceof liquidjs_1.Drop)) {
189
+ const record = value;
190
+ if (isRowRef(record) || (record.kind === 'person' && record.rowId !== undefined)) {
191
+ const collection = String(record.collectionId);
192
+ found.set(collection, (found.get(collection) ?? new Set()).add(String(record.rowId)));
193
+ }
194
+ else
195
+ for (const child of Object.values(record))
196
+ referencedRows(child, found);
197
+ }
198
+ return found;
199
+ }
200
+ /**
201
+ * The context for a site and the data files its page points at. People come from `site.json` `people` (with
202
+ * `sameAs`), so a person reads the same wherever they are referenced.
203
+ */
204
+ function linkContextFor(site, files = []) {
205
+ const rows = new Map();
206
+ for (const file of files) {
207
+ if (!file?.rows)
208
+ continue;
209
+ rows.set(String(file._id), new Map(file.rows.map((row) => [String(row._id), row])));
210
+ }
211
+ return {
212
+ pageIndex: site?.pageIndex,
213
+ documents: site?.documents,
214
+ row: (collectionId, rowId) => {
215
+ const person = site?.people?.[rowId];
216
+ return person ? { _id: rowId, ...person } : rows.get(collectionId)?.get(rowId);
217
+ },
218
+ };
219
+ }
220
+ /**
221
+ * An article's byline as templates and structured data read it, in order: `page.authors`. A person is their
222
+ * People row (`{{ author.name }}`, `{{ author.profile }}`); a guest is what was typed; the organisation is the
223
+ * site's. Each has `kind`, `name` and `url` (a person's profile page, else their first profile link).
224
+ */
225
+ function bylineAuthors(page, site, context) {
226
+ const authors = [];
227
+ for (const author of page.byline?.authors ?? []) {
228
+ if (author?.kind === 'person') {
229
+ const row = context.row?.(String(author.collectionId), String(author.rowId));
230
+ if (!row)
231
+ continue;
232
+ const person = new RowDrop(wrapLinks(structuredClone(row), context, 1));
233
+ const profile = person.profile;
234
+ const sameAs = Array.isArray(person.sameAs) ? person.sameAs : [];
235
+ person.kind = 'person';
236
+ person.url = profile && String(profile) ? String(profile) : sameAs[0];
237
+ authors.push(person);
238
+ }
239
+ else if (author?.kind === 'guest') {
240
+ authors.push({ kind: 'guest', name: author.name, url: author.url, jobTitle: author.jobTitle });
241
+ }
242
+ else if (author?.kind === 'organisation') {
243
+ const org = site?.site?.organisation;
244
+ if (org?.name)
245
+ authors.push({
246
+ kind: 'organisation',
247
+ name: org.name,
248
+ url: org.url || site?.site?.url,
249
+ photo: org.logo,
250
+ });
251
+ }
252
+ }
253
+ return authors;
254
+ }
61
255
  //# sourceMappingURL=links.js.map
package/lib/links.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":";;;AAmDA,8BAUC;AA7DD,uCAAgC;AAEhC,+FAA+F;AAC/F,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAElE;;;;;GAKG;AACH,MAAa,QAAS,SAAQ,eAAI;IAC9B,IAAI,CAAU;IACd,GAAG,CAAU;IACb,MAAM,CAAU;IAChB,UAAU,CAAU;IACpB,MAAM,CAAU;IAChB,KAAK,CAAU;IAEf,YAAY,IAA6B;QACrC,KAAK,EAAE,CAAC;QACR,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO;QACH,OAAO,IAAI,CAAC,GAAG,CAAC;IACpB,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC;IACpB,CAAC;IAED,2CAA2C;IAC3C,IAAI,MAAM;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,iCAAiC;IACjC,IAAI,IAAI;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,MAAM;QACF,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IACvB,CAAC;CACJ;AAlCD,4BAkCC;AAED,MAAM,MAAM,GAAG,CAAC,KAA8B,EAAE,EAAE,CAC9C,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC;AAEvG,iGAAiG;AACjG,SAAgB,SAAS,CAAI,KAAQ;IACjC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,eAAI;QAAE,OAAO,KAAK,CAAC;IAC/E,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IAAI,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAiB,CAAC;IAChE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { CeemsPage, CeemsSite } from './interfaces';
2
+ /** One `<meta>`: Open Graph tags take `property`, X's take `name`. */
3
+ export interface SocialMetaTag {
4
+ attribute: 'property' | 'name';
5
+ key: string;
6
+ content: string;
7
+ }
8
+ /**
9
+ * The page's link-preview tags: Open Graph, read by Facebook, LinkedIn, WhatsApp, Slack and others, and X's
10
+ * card. Built from what the page already publishes for search (title, description, social image, dates), so
11
+ * a template needs no more than `{% socialmeta %}`. X reads the title, description and image from Open Graph.
12
+ */
13
+ export declare function socialMeta(site: CeemsSite, page: CeemsPage): SocialMetaTag[];
14
+ /** The tags as HTML, their content escaped for an attribute. */
15
+ export declare function renderSocialMeta(tags: SocialMetaTag[]): string;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.socialMeta = socialMeta;
4
+ exports.renderSocialMeta = renderSocialMeta;
5
+ const structured_data_1 = require("./structured-data");
6
+ /**
7
+ * The page's link-preview tags: Open Graph, read by Facebook, LinkedIn, WhatsApp, Slack and others, and X's
8
+ * card. Built from what the page already publishes for search (title, description, social image, dates), so
9
+ * a template needs no more than `{% socialmeta %}`. X reads the title, description and image from Open Graph.
10
+ */
11
+ function socialMeta(site, page) {
12
+ const root = String(site.site?.url ?? '').replace(/\/+$/, '');
13
+ const path = page.fullpath && page.fullpath !== 'home' ? `/${page.fullpath}` : '/';
14
+ const absolute = (href) => /^https?:\/\//i.test(href) ? href : `${root}${href.startsWith('/') ? '' : '/'}${href}`;
15
+ const entity = (0, structured_data_1.entityOf)(page);
16
+ const image = (0, structured_data_1.imageUrl)(page.social);
17
+ const tags = [];
18
+ const og = (key, value) => {
19
+ const content = (0, structured_data_1.text)(value);
20
+ if (content)
21
+ tags.push({ attribute: 'property', key, content });
22
+ };
23
+ const x = (key, value) => {
24
+ const content = (0, structured_data_1.text)(value);
25
+ if (content)
26
+ tags.push({ attribute: 'name', key, content });
27
+ };
28
+ og('og:type', entity === 'ARTICLE' ? 'article' : entity === 'PERSON' ? 'profile' : 'website');
29
+ og('og:title', (0, structured_data_1.text)(page.title) ?? (0, structured_data_1.text)(page.name) ?? site.site?.name);
30
+ og('og:description', (0, structured_data_1.plain)(page.description));
31
+ if (root)
32
+ og('og:url', root + path);
33
+ og('og:site_name', site.site?.name);
34
+ if (image) {
35
+ og('og:image', absolute(image));
36
+ if (page.social?.width && page.social?.height) {
37
+ og('og:image:width', page.social.width);
38
+ og('og:image:height', page.social.height);
39
+ }
40
+ og('og:image:alt', page.social?.alt);
41
+ }
42
+ if (entity === 'ARTICLE') {
43
+ const published = (0, structured_data_1.isoDate)(page.date) ?? (0, structured_data_1.isoDate)(page.publishedAt);
44
+ og('article:published_time', published);
45
+ og('article:modified_time', (0, structured_data_1.isoDate)(page.updatedAt) ?? published);
46
+ for (const author of page.authors ?? []) {
47
+ const profile = (0, structured_data_1.text)(author.profile);
48
+ if (profile)
49
+ og('article:author', absolute(profile));
50
+ }
51
+ for (const tag of page.tags ?? [])
52
+ og('article:tag', tag);
53
+ }
54
+ x('twitter:card', image ? 'summary_large_image' : 'summary');
55
+ x('twitter:site', xHandle(site.site?.organisation?.sameAs));
56
+ return tags;
57
+ }
58
+ /** The organisation's X account, `@name`, from its profile links. */
59
+ function xHandle(sameAs) {
60
+ for (const url of sameAs ?? []) {
61
+ const match = /^https?:\/\/(?:www\.)?(?:x|twitter)\.com\/@?([A-Za-z0-9_]{1,15})\/?$/i.exec(url.trim());
62
+ if (match)
63
+ return `@${match[1]}`;
64
+ }
65
+ }
66
+ /** The tags as HTML, their content escaped for an attribute. */
67
+ function renderSocialMeta(tags) {
68
+ return tags.map((t) => `<meta ${t.attribute}="${t.key}" content="${escapeAttribute(t.content)}">`).join('');
69
+ }
70
+ function escapeAttribute(value) {
71
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
72
+ }
73
+ //# sourceMappingURL=social-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social-meta.js","sourceRoot":"","sources":["../src/social-meta.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { CeemsPage, CeemsSite, PageEntity, StructuredDataDefaults, StructuredDataType } from './interfaces';
1
+ import type { CeemsImage, CeemsPage, CeemsSite, PageEntity, StructuredDataDefaults, StructuredDataType } from './interfaces';
2
2
  /** The types a page or the site's defaults can switch on, in the order the tag outputs them. */
3
3
  export declare const STRUCTURED_DATA_TYPES: readonly StructuredDataType[];
4
4
  /** What `{% structureddata %}` can be limited to: the types, and the page's entity (its product, event…). */
@@ -25,4 +25,11 @@ export declare function structuredDataGraph(site: CeemsSite, page: CeemsPage, on
25
25
  };
26
26
  /** JSON for a `<script>`: `<`, `>` and `&` are escaped so content can't end the script. */
27
27
  export declare function serialiseJsonLd(value: unknown): string;
28
+ export declare function imageUrl(image?: CeemsImage | null): string | undefined;
29
+ /** A date published as an ISO string (or a `Date`, for custom pages) as an ISO string. */
30
+ export declare function isoDate(value: unknown): string | undefined;
31
+ /** A non-empty trimmed string, else `undefined`. Stored links (`LinkDrop`) read as their address. */
32
+ export declare function text(value: unknown): string | undefined;
33
+ /** Rich text as plain text, for properties that take no markup. */
34
+ export declare function plain(value: unknown): unknown;
28
35
  export {};