@teleporthq/teleport-plugin-next-data-source 0.43.74 → 0.44.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/__tests__/_helpers/pagination-widget-fixture.ts +95 -5
- package/__tests__/blog-adjacent-arranged.test.ts +176 -0
- package/__tests__/blog-post-contents.test.ts +212 -0
- package/__tests__/blog-post-page-enrichment.test.ts +28 -5
- package/__tests__/blog-post-picture-stand-in.test.ts +48 -0
- package/__tests__/blog-post-reading-time.test.ts +55 -0
- package/__tests__/booking-item-transform.test.ts +120 -0
- package/__tests__/browser-row-policy.test.ts +13 -3
- package/__tests__/content-preset-wiring.test.ts +108 -0
- package/__tests__/fetchers.test.ts +1 -1
- package/__tests__/fixed-list-sort.test.ts +183 -0
- package/__tests__/infinite-scroll.test.ts +154 -5
- package/__tests__/list-sort-order.test.ts +61 -0
- package/__tests__/null-filter-where.test.ts +132 -0
- package/__tests__/pagination-filter-groups.test.ts +186 -0
- package/__tests__/pg-date-parser.test.ts +73 -0
- package/__tests__/plain-list-sort.test.ts +215 -0
- package/__tests__/product-price-sort.ts +58 -25
- package/__tests__/range-control.test.ts +159 -0
- package/__tests__/rest-api-key-header.test.ts +18 -0
- package/__tests__/search-input-marker.test.ts +86 -0
- package/__tests__/search-url-sync.test.ts +61 -16
- package/__tests__/shared-resource-prefetch.test.ts +65 -0
- package/__tests__/table-read-route-protected-tables.test.ts +33 -3
- package/dist/cjs/count-effect.d.ts +7 -1
- package/dist/cjs/count-effect.d.ts.map +1 -1
- package/dist/cjs/count-effect.js +15 -5
- package/dist/cjs/count-effect.js.map +1 -1
- package/dist/cjs/fetchers/pg-client-import.d.ts +12 -0
- package/dist/cjs/fetchers/pg-client-import.d.ts.map +1 -0
- package/dist/cjs/fetchers/pg-client-import.js +15 -0
- package/dist/cjs/fetchers/pg-client-import.js.map +1 -0
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +4 -1
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +2 -1
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +3 -1
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +5 -2
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +7 -6
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.js +7 -33
- package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -1
- package/dist/cjs/filter-utils.d.ts +19 -7
- package/dist/cjs/filter-utils.d.ts.map +1 -1
- package/dist/cjs/filter-utils.js +49 -13
- package/dist/cjs/filter-utils.js.map +1 -1
- package/dist/cjs/infinite-scroll.d.ts +25 -2
- package/dist/cjs/infinite-scroll.d.ts.map +1 -1
- package/dist/cjs/infinite-scroll.js +144 -33
- package/dist/cjs/infinite-scroll.js.map +1 -1
- package/dist/cjs/list-sort-order.d.ts +14 -0
- package/dist/cjs/list-sort-order.d.ts.map +1 -0
- package/dist/cjs/list-sort-order.js +36 -0
- package/dist/cjs/list-sort-order.js.map +1 -0
- package/dist/cjs/pagination-controls.d.ts +22 -3
- package/dist/cjs/pagination-controls.d.ts.map +1 -1
- package/dist/cjs/pagination-controls.js +29 -3
- package/dist/cjs/pagination-controls.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +267 -212
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/product-price-sort.d.ts +0 -9
- package/dist/cjs/product-price-sort.d.ts.map +1 -1
- package/dist/cjs/product-price-sort.js +1 -13
- package/dist/cjs/product-price-sort.js.map +1 -1
- package/dist/cjs/range-control.d.ts +45 -0
- package/dist/cjs/range-control.d.ts.map +1 -0
- package/dist/cjs/range-control.js +165 -0
- package/dist/cjs/range-control.js.map +1 -0
- package/dist/cjs/row-order.d.ts +20 -0
- package/dist/cjs/row-order.d.ts.map +1 -0
- package/dist/cjs/row-order.js +46 -0
- package/dist/cjs/row-order.js.map +1 -0
- package/dist/cjs/search-input.d.ts +42 -0
- package/dist/cjs/search-input.d.ts.map +1 -0
- package/dist/cjs/search-input.js +158 -0
- package/dist/cjs/search-input.js.map +1 -0
- package/dist/cjs/sort-utils.d.ts +10 -0
- package/dist/cjs/sort-utils.d.ts.map +1 -1
- package/dist/cjs/sort-utils.js +22 -1
- package/dist/cjs/sort-utils.js.map +1 -1
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts +11 -1
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-adjacent-posts.js +14 -1
- package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -1
- package/dist/cjs/transformations/blog-comments.d.ts +1 -1
- package/dist/cjs/transformations/blog-comments.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-comments.js +1 -1
- package/dist/cjs/transformations/blog-comments.js.map +1 -1
- package/dist/cjs/transformations/blog-post.d.ts +14 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +4 -1
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/booking-item.d.ts +24 -0
- package/dist/cjs/transformations/booking-item.d.ts.map +1 -0
- package/dist/cjs/transformations/booking-item.js +27 -0
- package/dist/cjs/transformations/booking-item.js.map +1 -0
- package/dist/cjs/transformations/heading-anchors.d.ts +32 -8
- package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -1
- package/dist/cjs/transformations/heading-anchors.js +34 -10
- package/dist/cjs/transformations/heading-anchors.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +27 -13
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +73 -16
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +52 -3
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/count-effect.d.ts +7 -1
- package/dist/esm/count-effect.d.ts.map +1 -1
- package/dist/esm/count-effect.js +15 -5
- package/dist/esm/count-effect.js.map +1 -1
- package/dist/esm/fetchers/pg-client-import.d.ts +12 -0
- package/dist/esm/fetchers/pg-client-import.d.ts.map +1 -0
- package/dist/esm/fetchers/pg-client-import.js +12 -0
- package/dist/esm/fetchers/pg-client-import.js.map +1 -0
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +5 -2
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +2 -1
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +3 -1
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +6 -3
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts +7 -6
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.js +5 -31
- package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -1
- package/dist/esm/filter-utils.d.ts +19 -7
- package/dist/esm/filter-utils.d.ts.map +1 -1
- package/dist/esm/filter-utils.js +46 -12
- package/dist/esm/filter-utils.js.map +1 -1
- package/dist/esm/infinite-scroll.d.ts +25 -2
- package/dist/esm/infinite-scroll.d.ts.map +1 -1
- package/dist/esm/infinite-scroll.js +142 -32
- package/dist/esm/infinite-scroll.js.map +1 -1
- package/dist/esm/list-sort-order.d.ts +14 -0
- package/dist/esm/list-sort-order.d.ts.map +1 -0
- package/dist/esm/list-sort-order.js +32 -0
- package/dist/esm/list-sort-order.js.map +1 -0
- package/dist/esm/pagination-controls.d.ts +22 -3
- package/dist/esm/pagination-controls.d.ts.map +1 -1
- package/dist/esm/pagination-controls.js +28 -2
- package/dist/esm/pagination-controls.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +271 -216
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/product-price-sort.d.ts +0 -9
- package/dist/esm/product-price-sort.d.ts.map +1 -1
- package/dist/esm/product-price-sort.js +0 -11
- package/dist/esm/product-price-sort.js.map +1 -1
- package/dist/esm/range-control.d.ts +45 -0
- package/dist/esm/range-control.d.ts.map +1 -0
- package/dist/esm/range-control.js +137 -0
- package/dist/esm/range-control.js.map +1 -0
- package/dist/esm/row-order.d.ts +20 -0
- package/dist/esm/row-order.d.ts.map +1 -0
- package/dist/esm/row-order.js +41 -0
- package/dist/esm/row-order.js.map +1 -0
- package/dist/esm/search-input.d.ts +42 -0
- package/dist/esm/search-input.d.ts.map +1 -0
- package/dist/esm/search-input.js +129 -0
- package/dist/esm/search-input.js.map +1 -0
- package/dist/esm/sort-utils.d.ts +10 -0
- package/dist/esm/sort-utils.d.ts.map +1 -1
- package/dist/esm/sort-utils.js +20 -0
- package/dist/esm/sort-utils.js.map +1 -1
- package/dist/esm/transformations/blog-adjacent-posts.d.ts +11 -1
- package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -1
- package/dist/esm/transformations/blog-adjacent-posts.js +14 -1
- package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -1
- package/dist/esm/transformations/blog-comments.d.ts +1 -1
- package/dist/esm/transformations/blog-comments.d.ts.map +1 -1
- package/dist/esm/transformations/blog-comments.js +1 -1
- package/dist/esm/transformations/blog-comments.js.map +1 -1
- package/dist/esm/transformations/blog-post.d.ts +14 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +4 -1
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/booking-item.d.ts +24 -0
- package/dist/esm/transformations/booking-item.d.ts.map +1 -0
- package/dist/esm/transformations/booking-item.js +23 -0
- package/dist/esm/transformations/booking-item.js.map +1 -0
- package/dist/esm/transformations/heading-anchors.d.ts +32 -8
- package/dist/esm/transformations/heading-anchors.d.ts.map +1 -1
- package/dist/esm/transformations/heading-anchors.js +33 -9
- package/dist/esm/transformations/heading-anchors.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +27 -13
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +72 -16
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +52 -3
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/count-effect.ts +31 -8
- package/src/fetchers/pg-client-import.ts +16 -0
- package/src/fetchers/postgresql.ts +25 -23
- package/src/fetchers/raw-query.ts +2 -1
- package/src/fetchers/rest-api.ts +3 -1
- package/src/fetchers/teleport.ts +22 -23
- package/src/fetchers/utils/browser-row-policy.ts +10 -20
- package/src/filter-utils.ts +102 -28
- package/src/infinite-scroll.ts +240 -79
- package/src/list-sort-order.ts +34 -0
- package/src/pagination-controls.ts +26 -3
- package/src/pagination-plugin.ts +370 -306
- package/src/product-price-sort.ts +0 -11
- package/src/range-control.ts +207 -0
- package/src/row-order.ts +55 -0
- package/src/search-input.ts +190 -0
- package/src/sort-utils.ts +25 -0
- package/src/transformations/blog-adjacent-posts.ts +89 -3
- package/src/transformations/blog-comments.ts +5 -5
- package/src/transformations/blog-post.ts +64 -13
- package/src/transformations/booking-item.ts +71 -0
- package/src/transformations/heading-anchors.ts +79 -16
- package/src/transformations/index.ts +104 -14
- package/src/utils.ts +74 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { UIDLEcommerceCategory } from '@teleporthq/teleport-types'
|
|
2
|
+
import { ContentTables, RichTextContentCodegen } from '@teleporthq/teleport-shared'
|
|
2
3
|
import { generateCategoryTaxonomyCode } from './category-taxonomy'
|
|
3
4
|
import { generateAdjacentPostsCode } from './blog-adjacent-posts'
|
|
4
5
|
import { generateBlogCommentsCode } from './blog-comments'
|
|
@@ -6,10 +7,23 @@ import { generateHeadingAnchorsCode } from './heading-anchors'
|
|
|
6
7
|
|
|
7
8
|
/** What the blog-post transform bakes in at export time beyond the row itself. */
|
|
8
9
|
export interface BlogPostTransformOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The preset's tables (the Blog's by default): the posts table the related
|
|
12
|
+
* and adjacent posts are read from, the comments table a post page counts.
|
|
13
|
+
*/
|
|
14
|
+
tables?: ContentTables.ContentTables
|
|
9
15
|
/** Category taxonomy — lives only in the UIDL, there is no DB table for it. */
|
|
10
16
|
categories?: UIDLEcommerceCategory[]
|
|
11
17
|
/** Content headings get ids and `#` links — see `heading-anchors.ts`. */
|
|
12
18
|
headingAnchors?: boolean
|
|
19
|
+
/**
|
|
20
|
+
* The post pages carry a Contents list: the content's headings get their ids
|
|
21
|
+
* (the `#` links only with `headingAnchors`) and a post page's post its
|
|
22
|
+
* `sections` — see `blogContentsSectionsOf`.
|
|
23
|
+
*/
|
|
24
|
+
contents?: boolean
|
|
25
|
+
/** Manual Order: Previous / Next walk the post's category as arranged — see `blog-adjacent-posts.ts`. */
|
|
26
|
+
arranged?: boolean
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
/**
|
|
@@ -20,15 +34,28 @@ export interface BlogPostTransformOptions {
|
|
|
20
34
|
export const generateBlogPostTransformationCode = (
|
|
21
35
|
options: BlogPostTransformOptions = {}
|
|
22
36
|
): string => {
|
|
37
|
+
const tables = options.tables ?? ContentTables.contentTablesByKey('blog')
|
|
23
38
|
return `
|
|
24
39
|
// ============================================================
|
|
25
40
|
// Blog Post Transformation
|
|
26
41
|
// ============================================================
|
|
27
42
|
${generateCategoryTaxonomyCode('BLOG_CATEGORIES_BY_ID', options.categories)}
|
|
28
|
-
${generateBlogCommentsCode()}
|
|
29
|
-
${generateAdjacentPostsCode()}
|
|
43
|
+
${generateBlogCommentsCode(tables.comments ?? `${tables.posts}_comments`)}
|
|
44
|
+
${generateAdjacentPostsCode(tables.posts, options.arranged === true)}
|
|
30
45
|
${generateHeadingAnchorsCode()}
|
|
46
|
+
${RichTextContentCodegen.generateLegacyRichTextNormalizerCode()}
|
|
31
47
|
var BLOG_HEADING_ANCHORS = ${options.headingAnchors === true}
|
|
48
|
+
var BLOG_CONTENTS = ${options.contents === true}
|
|
49
|
+
var BLOG_PICTURE_STAND_IN = ${tables.pictureStandIn !== false}
|
|
50
|
+
|
|
51
|
+
// A post's reading time from its body, for one that stores none: its words at
|
|
52
|
+
// about 200 a minute, at least one — what the editor's post form computes. A
|
|
53
|
+
// card or post page otherwise printed "min read" with no number before it.
|
|
54
|
+
// MUST mirror readingTimeOf in teleport-gui packages/renderer/src/utils/blog-posts.ts.
|
|
55
|
+
function blogReadingTimeOf(html) {
|
|
56
|
+
var text = String(html || '').replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()
|
|
57
|
+
return text ? Math.max(1, Math.ceil(text.split(' ').length / 200)) : null
|
|
58
|
+
}
|
|
32
59
|
|
|
33
60
|
function buildBlogPost(record, options) {
|
|
34
61
|
if (!record || typeof record !== 'object') return record
|
|
@@ -42,8 +69,21 @@ function buildBlogPost(record, options) {
|
|
|
42
69
|
// i18n-resolved text fields
|
|
43
70
|
var title = resolveI18nField(record, 'title', 'title', currentLang, mainLang) || ''
|
|
44
71
|
var slug = resolveI18nField(record, 'slug', 'slug', currentLang, mainLang) || ''
|
|
45
|
-
|
|
46
|
-
|
|
72
|
+
// A body stored before the rich-text contract (the studio's flat list lines,
|
|
73
|
+
// the admin panel's no-break spaces) reads as stored HTML from here on.
|
|
74
|
+
var content = normalizeLegacyRichTextHtml(
|
|
75
|
+
resolveI18nField(record, 'content', 'content', currentLang, mainLang) || ''
|
|
76
|
+
)
|
|
77
|
+
// Counted before the heading anchors add their marks.
|
|
78
|
+
var bodyReadingTime = blogReadingTimeOf(content)
|
|
79
|
+
// The Contents list links to the headings, so they get their ids for it too
|
|
80
|
+
// — the # links only with the heading anchors on. Its sections ride on a
|
|
81
|
+
// post page's own fetch only (options.details): a listing draws no list, and
|
|
82
|
+
// every card of it would otherwise carry one.
|
|
83
|
+
var contentsHeadings = BLOG_CONTENTS && options.details === true ? [] : null
|
|
84
|
+
if (BLOG_HEADING_ANCHORS || BLOG_CONTENTS) {
|
|
85
|
+
content = addHeadingAnchors(content, { links: BLOG_HEADING_ANCHORS, sections: contentsHeadings })
|
|
86
|
+
}
|
|
47
87
|
var excerpt = resolveI18nField(record, 'excerpt', 'excerpt', currentLang, mainLang) || ''
|
|
48
88
|
var category = resolveI18nField(record, 'category', 'category', currentLang, mainLang) || null
|
|
49
89
|
|
|
@@ -77,11 +117,15 @@ function buildBlogPost(record, options) {
|
|
|
77
117
|
rawFeaturedImage === TELEPORT_DEFAULT_FEATURED_IMG ||
|
|
78
118
|
featuredImageUrl === TELEPORT_DEFAULT_FEATURED_IMG
|
|
79
119
|
) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
120
|
+
// No picture of its own: the grey "No image" stand-in, or none at all for a
|
|
121
|
+
// preset whose posts are text (ContentTables.pictureStandIn) — its card and
|
|
122
|
+
// post page show the picture only when there is one.
|
|
123
|
+
featuredImageUrl = BLOG_PICTURE_STAND_IN
|
|
124
|
+
? 'data:image/svg+xml;charset=utf-8,' +
|
|
125
|
+
encodeURIComponent(
|
|
126
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="400" height="240" viewBox="0 0 400 240"><rect fill="#e5e7eb" width="400" height="240"/><text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#9ca3af" font-family="system-ui,sans-serif" font-size="13">No image</text></svg>'
|
|
127
|
+
)
|
|
128
|
+
: null
|
|
85
129
|
}
|
|
86
130
|
|
|
87
131
|
var rawAuthorAvatar = pickFirst(record.author_avatar_url, record.authorAvatarUrl)
|
|
@@ -90,8 +134,11 @@ function buildBlogPost(record, options) {
|
|
|
90
134
|
// Simple pass-through fields
|
|
91
135
|
var authorName = pickFirst(record.author_name, record.authorName)
|
|
92
136
|
var authorInitials = blogAuthorInitials(authorName)
|
|
93
|
-
|
|
137
|
+
// The author's email never leaves the server: a visitor's API read hides it
|
|
138
|
+
// (browser-row-policy.ts), and the page data a post or listing page ships
|
|
139
|
+
// is built here, so it is not part of the post a page sees either.
|
|
94
140
|
var readingTimeMinutes = safeNumber(pickFirst(record.reading_time_minutes, record.readingTimeMinutes), null)
|
|
141
|
+
if (!(readingTimeMinutes > 0)) readingTimeMinutes = bodyReadingTime
|
|
95
142
|
|
|
96
143
|
// Tags - i18n-resolved then parsed as JSON array
|
|
97
144
|
var rawTags = resolveI18nField(record, 'tags', 'tags', currentLang, mainLang)
|
|
@@ -207,15 +254,16 @@ function buildBlogPost(record, options) {
|
|
|
207
254
|
galleryImages: galleryImages,
|
|
208
255
|
allImages: allImages,
|
|
209
256
|
authorName: authorName,
|
|
210
|
-
authorEmail: authorEmail,
|
|
211
257
|
author_name: authorName,
|
|
212
|
-
author_email: authorEmail,
|
|
213
258
|
authorAvatarUrl: authorAvatarUrl,
|
|
214
259
|
authorBio: authorBio,
|
|
215
260
|
authorInitials: authorInitials,
|
|
216
261
|
metaTitle: metaTitle,
|
|
217
262
|
metaDescription: metaDescription,
|
|
218
263
|
readingTimeMinutes: readingTimeMinutes,
|
|
264
|
+
// The post's sections, what its page's Contents list links to — see
|
|
265
|
+
// blogContentsSectionsOf; empty off a post page or under three sections.
|
|
266
|
+
sections: contentsHeadings ? blogContentsSectionsOf(contentsHeadings) : [],
|
|
219
267
|
isFeatured: isFeatured,
|
|
220
268
|
allowComments: allowComments,
|
|
221
269
|
comments: comments,
|
|
@@ -231,6 +279,9 @@ function buildBlogPost(record, options) {
|
|
|
231
279
|
createdAt: createdAt,
|
|
232
280
|
updatedAt: updatedAt,
|
|
233
281
|
created: created,
|
|
282
|
+
// The post's place among its primary category's posts (Manual Order), or
|
|
283
|
+
// null: what the category pages order by.
|
|
284
|
+
sortOrder: safeNumber(pickFirst(record.sort_order, record.sortOrder), null),
|
|
234
285
|
}
|
|
235
286
|
}
|
|
236
287
|
|
|
@@ -261,7 +312,7 @@ async function getRelatedPostsMap(getClientFn, records) {
|
|
|
261
312
|
try {
|
|
262
313
|
client = getClientFn()
|
|
263
314
|
await client.connect()
|
|
264
|
-
var result = await client.query('SELECT * FROM
|
|
315
|
+
var result = await client.query('SELECT * FROM ${tables.posts} WHERE id = ANY($1)', [
|
|
265
316
|
wantedIds,
|
|
266
317
|
])
|
|
267
318
|
if (result && result.rows) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { UIDLEcommerceCategory } from '@teleporthq/teleport-types'
|
|
2
|
+
import { generateCategoryTaxonomyCode } from './category-taxonomy'
|
|
3
|
+
|
|
4
|
+
export interface BookingItemTransformOptions {
|
|
5
|
+
/** The booking items' category taxonomy — see `bookingSettings.categories`. */
|
|
6
|
+
categories?: UIDLEcommerceCategory[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates JavaScript code for the `teleport_booking_items` row
|
|
11
|
+
* transformation — the things a Bookings site lets people book.
|
|
12
|
+
*
|
|
13
|
+
* The row is kept whole (every column, snake_case: the booking pages bind
|
|
14
|
+
* them as stored) and its translatable texts — `name`, `description` (rich
|
|
15
|
+
* text) and `location` — are the page's language when the row has them there
|
|
16
|
+
* (`es_name`, …), the main language otherwise. Its `category` is the name its
|
|
17
|
+
* first category has in the page's language, read off the taxonomy baked in
|
|
18
|
+
* here (the row's stored main name while none of its ids is a category). That
|
|
19
|
+
* is all a booking page needs to show an item in the visitor's language; the
|
|
20
|
+
* booking statements localise what they read on their own.
|
|
21
|
+
*
|
|
22
|
+
* MUST mirror `localizeBookingItem` in the editor
|
|
23
|
+
* (teleport-gui features/bookings/utils/localize-booking-item.ts), which is
|
|
24
|
+
* what the canvas renders from.
|
|
25
|
+
*/
|
|
26
|
+
export const generateBookingItemTransformationCode = (
|
|
27
|
+
options: BookingItemTransformOptions = {}
|
|
28
|
+
): string => {
|
|
29
|
+
return `
|
|
30
|
+
// ============================================================
|
|
31
|
+
// Booking Item Transformation
|
|
32
|
+
// ============================================================
|
|
33
|
+
|
|
34
|
+
var BOOKING_ITEM_TRANSLATABLE_COLUMNS = ['name', 'description', 'location']
|
|
35
|
+
${generateCategoryTaxonomyCode('BOOKING_CATEGORIES_BY_ID', options.categories)}
|
|
36
|
+
|
|
37
|
+
function buildBookingItem(record, options) {
|
|
38
|
+
if (!record || typeof record !== 'object') return record
|
|
39
|
+
options = options || {}
|
|
40
|
+
var currentLang = options.currentLanguage || null
|
|
41
|
+
var mainLang = options.mainLanguage || null
|
|
42
|
+
|
|
43
|
+
var out = {}
|
|
44
|
+
for (var key in record) {
|
|
45
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) out[key] = record[key]
|
|
46
|
+
}
|
|
47
|
+
for (var i = 0; i < BOOKING_ITEM_TRANSLATABLE_COLUMNS.length; i++) {
|
|
48
|
+
var column = BOOKING_ITEM_TRANSLATABLE_COLUMNS[i]
|
|
49
|
+
var value = resolveI18nField(record, column, column, currentLang, mainLang)
|
|
50
|
+
out[column] = value === null || value === undefined ? record[column] : value
|
|
51
|
+
}
|
|
52
|
+
if ('category' in record || 'category_ids' in record) {
|
|
53
|
+
var stored = typeof record.category === 'string' ? record.category : ''
|
|
54
|
+
var assigned = resolveAssignedCategories(
|
|
55
|
+
record.category_ids,
|
|
56
|
+
BOOKING_CATEGORIES_BY_ID,
|
|
57
|
+
currentLang,
|
|
58
|
+
mainLang,
|
|
59
|
+
stored
|
|
60
|
+
)
|
|
61
|
+
out.category = assigned.length > 0 ? assigned[0].name : stored
|
|
62
|
+
}
|
|
63
|
+
return out
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function transformBookingItems(records, options) {
|
|
67
|
+
if (!Array.isArray(records)) return []
|
|
68
|
+
return records.map(function(record) { return buildBookingItem(record, options) })
|
|
69
|
+
}
|
|
70
|
+
`
|
|
71
|
+
}
|
|
@@ -9,22 +9,51 @@
|
|
|
9
9
|
export const BLOG_HEADING_ANCHOR_CLASS = 'tq-heading-anchor'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* gets an `id` (kept when it already has one, otherwise a slug of its text,
|
|
14
|
-
* unique within the post) and a trailing `#` link to itself, so a section can
|
|
15
|
-
* be linked to (`/blog/post#setup`) and the link works on the very first load —
|
|
16
|
-
* the ids are in the server-rendered HTML.
|
|
12
|
+
* The class of the link a post's content heading's own text becomes.
|
|
17
13
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* ⛔ PAIRED with `BLOG_HEADING_LINK_CLASS` in teleport-gui
|
|
15
|
+
* `features/blog/utils/elements/heading-anchor-styles.ts`, whose stylesheet
|
|
16
|
+
* keeps the heading looking like a heading rather than like a link.
|
|
17
|
+
*/
|
|
18
|
+
export const BLOG_HEADING_LINK_CLASS = 'tq-heading-link'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The fewest sections a post's Contents list is drawn for: a shorter post is
|
|
22
|
+
* taken in at a glance.
|
|
23
|
+
*
|
|
24
|
+
* ⛔ PAIRED with `CONTENTS_MIN_SECTIONS` in teleport-gui
|
|
25
|
+
* `packages/renderer/src/utils/blog-posts.ts`, the canvas's copy.
|
|
26
|
+
*/
|
|
27
|
+
export const CONTENTS_MIN_SECTIONS = 3
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generates `addHeadingAnchors(html, options)`: every `<h2>`–`<h6>` of a post's
|
|
31
|
+
* content gets an `id` (kept when it already has one, otherwise a slug of its
|
|
32
|
+
* text, unique within the post), its text becomes a link to itself and a `#`
|
|
33
|
+
* follows it, shown on hover, so a section can be linked to
|
|
34
|
+
* (`/blog/post#setup`) by clicking its heading, and the link works on the very
|
|
35
|
+
* first load — the ids are in the server-rendered HTML. A heading that holds a
|
|
36
|
+
* link of its own keeps its text as it is (a link cannot hold another) and
|
|
37
|
+
* links through the `#` alone.
|
|
38
|
+
*
|
|
39
|
+
* `options.links: false` gives the headings their ids without the links —
|
|
40
|
+
* what a post page's Contents list needs when the heading anchors are off.
|
|
41
|
+
* `options.sections`, an array, collects every heading as `{ level, id, text }`
|
|
42
|
+
* in reading order, and `blogContentsSectionsOf` turns those into the list.
|
|
43
|
+
*
|
|
44
|
+
* The heading's text is the link a screen reader announces and the keyboard
|
|
45
|
+
* reaches; the `#` repeats it, so it is hidden from assistive technology and
|
|
46
|
+
* left out of the tab order.
|
|
21
47
|
*
|
|
22
48
|
* Idempotent: a heading that already carries the link is left alone. `h1` is
|
|
23
49
|
* the post title's level and is never touched.
|
|
24
50
|
*/
|
|
25
51
|
export const generateHeadingAnchorsCode = (): string => `
|
|
26
52
|
var BLOG_HEADING_ANCHOR_CLASS = '${BLOG_HEADING_ANCHOR_CLASS}'
|
|
53
|
+
var BLOG_HEADING_LINK_CLASS = '${BLOG_HEADING_LINK_CLASS}'
|
|
27
54
|
var BLOG_HEADING_ID_PATTERN = /\\sid\\s*=\\s*("([^"]*)"|'([^']*)')/i
|
|
55
|
+
var BLOG_HEADING_ANCHOR_LINK_PATTERN = /<a\\s[^>]*class="${BLOG_HEADING_ANCHOR_CLASS}"[^>]*>[\\s\\S]*?<\\/a>/gi
|
|
56
|
+
var BLOG_CONTENTS_MIN_SECTIONS = ${CONTENTS_MIN_SECTIONS}
|
|
28
57
|
|
|
29
58
|
function escapeHeadingAttribute(value) {
|
|
30
59
|
return String(value)
|
|
@@ -52,11 +81,15 @@ function blogHeadingSlug(text, used) {
|
|
|
52
81
|
return slug
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
function addHeadingAnchors(html) {
|
|
84
|
+
function addHeadingAnchors(html, options) {
|
|
85
|
+
options = options || {}
|
|
86
|
+
var withLinks = options.links !== false
|
|
87
|
+
var sections = Array.isArray(options.sections) ? options.sections : null
|
|
56
88
|
if (typeof html !== 'string' || !/<h[2-6][\\s>]/i.test(html)) return html
|
|
57
89
|
// Every id the content already uses is taken, so a generated one never
|
|
58
|
-
// duplicates it.
|
|
59
|
-
|
|
90
|
+
// duplicates it. No prototype: a heading titled "Constructor" is free to be
|
|
91
|
+
// #constructor.
|
|
92
|
+
var used = Object.create(null)
|
|
60
93
|
var existingIds = new RegExp(BLOG_HEADING_ID_PATTERN.source, 'gi')
|
|
61
94
|
var existing
|
|
62
95
|
while ((existing = existingIds.exec(html)) !== null) {
|
|
@@ -64,22 +97,52 @@ function addHeadingAnchors(html) {
|
|
|
64
97
|
}
|
|
65
98
|
|
|
66
99
|
return html.replace(/<h([2-6])(\\s[^>]*)?>([\\s\\S]*?)<\\/h\\1>/gi, function(match, level, rawAttrs, inner) {
|
|
67
|
-
if (inner.indexOf(BLOG_HEADING_ANCHOR_CLASS) !== -1) return match
|
|
68
|
-
var text = stripHtmlTags(inner)
|
|
69
|
-
if (!text) return match
|
|
70
100
|
var attrs = rawAttrs || ''
|
|
71
101
|
var idMatch = attrs.match(BLOG_HEADING_ID_PATTERN)
|
|
72
102
|
var id = idMatch ? decodeHtmlEntities(idMatch[2] || idMatch[3] || '') : ''
|
|
103
|
+
if (inner.indexOf(BLOG_HEADING_ANCHOR_CLASS) !== -1) {
|
|
104
|
+
if (sections && id) {
|
|
105
|
+
var anchoredText = stripHtmlTags(inner.replace(BLOG_HEADING_ANCHOR_LINK_PATTERN, ''))
|
|
106
|
+
if (anchoredText) sections.push({ level: Number(level), id: id, text: anchoredText })
|
|
107
|
+
}
|
|
108
|
+
return match
|
|
109
|
+
}
|
|
110
|
+
var text = stripHtmlTags(inner)
|
|
111
|
+
if (!text) return match
|
|
73
112
|
if (!id) {
|
|
74
113
|
id = blogHeadingSlug(text, used)
|
|
75
114
|
attrs = ' id="' + escapeHeadingAttribute(id) + '"' + attrs
|
|
76
115
|
}
|
|
116
|
+
if (sections) sections.push({ level: Number(level), id: id, text: text })
|
|
117
|
+
if (!withLinks) return '<h' + level + attrs + '>' + inner + '</h' + level + '>'
|
|
118
|
+
var href = '#' + escapeHeadingAttribute(id)
|
|
119
|
+
var ownText = /<a[\\s>]/i.test(inner)
|
|
120
|
+
? inner
|
|
121
|
+
: '<a class="' + BLOG_HEADING_LINK_CLASS + '" href="' + href + '">' + inner + '</a>'
|
|
77
122
|
return (
|
|
78
|
-
'<h' + level + attrs + '>' +
|
|
79
|
-
'<a class="' + BLOG_HEADING_ANCHOR_CLASS + '" href="
|
|
123
|
+
'<h' + level + attrs + '>' + ownText +
|
|
124
|
+
'<a class="' + BLOG_HEADING_ANCHOR_CLASS + '" href="' + href +
|
|
80
125
|
'" aria-hidden="true" tabindex="-1">#</a>' +
|
|
81
126
|
'</h' + level + '>'
|
|
82
127
|
)
|
|
83
128
|
})
|
|
84
129
|
}
|
|
130
|
+
|
|
131
|
+
// A post page's Contents list: the post's sections, i.e. its headings of the
|
|
132
|
+
// topmost level it uses, each with the address that jumps to it; none for a
|
|
133
|
+
// post of fewer than BLOG_CONTENTS_MIN_SECTIONS sections.
|
|
134
|
+
// MUST mirror contentsSectionsOf in teleport-gui packages/renderer/src/utils/blog-posts.ts.
|
|
135
|
+
function blogContentsSectionsOf(headings) {
|
|
136
|
+
if (!Array.isArray(headings) || headings.length === 0) return []
|
|
137
|
+
var top = 6
|
|
138
|
+
for (var i = 0; i < headings.length; i++) {
|
|
139
|
+
if (headings[i].level < top) top = headings[i].level
|
|
140
|
+
}
|
|
141
|
+
var sections = []
|
|
142
|
+
for (var j = 0; j < headings.length; j++) {
|
|
143
|
+
if (headings[j].level !== top) continue
|
|
144
|
+
sections.push({ id: headings[j].id, title: headings[j].text, href: '#' + headings[j].id })
|
|
145
|
+
}
|
|
146
|
+
return sections.length >= BLOG_CONTENTS_MIN_SECTIONS ? sections : []
|
|
147
|
+
}
|
|
85
148
|
`
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
GeneratorOptions,
|
|
3
|
+
UIDLBlogSettings,
|
|
3
4
|
UIDLAuthentication,
|
|
4
5
|
UIDLEcommerceCategory,
|
|
5
6
|
} from '@teleporthq/teleport-types'
|
|
6
|
-
import { StorefrontTax, TableAccess } from '@teleporthq/teleport-shared'
|
|
7
|
+
import { ContentTables, StorefrontTax, TableAccess } from '@teleporthq/teleport-shared'
|
|
7
8
|
import { generateSharedTransformationCode } from './shared-utils'
|
|
8
9
|
import { generateBlogPostTransformationCode } from './blog-post'
|
|
9
10
|
import { generateCustomPageTransformationCode } from './custom-page'
|
|
11
|
+
import { generateBookingItemTransformationCode } from './booking-item'
|
|
10
12
|
import {
|
|
11
13
|
generateEcommerceProductTransformationCode,
|
|
12
14
|
type EcommerceProductTransformOptions,
|
|
@@ -26,9 +28,26 @@ export type { EcommerceProductTransformOptions }
|
|
|
26
28
|
* to serve whichever transform that resolves to — the product fields are ignored
|
|
27
29
|
* for a blog table and vice versa.
|
|
28
30
|
*/
|
|
31
|
+
/** What a content preset bakes into its post transform — see `UIDLBlogSettings`. */
|
|
32
|
+
export interface ContentPresetTransformSettings {
|
|
33
|
+
categories?: UIDLEcommerceCategory[]
|
|
34
|
+
headingAnchors?: boolean
|
|
35
|
+
contents?: boolean
|
|
36
|
+
comments?: boolean
|
|
37
|
+
order?: 'manual'
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
export interface EntityTransformOptions extends EcommerceProductTransformOptions {
|
|
41
|
+
/**
|
|
42
|
+
* The settings of every content preset, by preset key — the post transform
|
|
43
|
+
* of a table reads its own preset's (`contentSettingsFor`). The `blog*`
|
|
44
|
+
* fields below are the Blog's, kept for the callers that name them.
|
|
45
|
+
*/
|
|
46
|
+
contentSettings?: Partial<Record<ContentTables.ContentPresetKey, ContentPresetTransformSettings>>
|
|
30
47
|
/** Blog post-category taxonomy — see `blogSettings.categories`. */
|
|
31
48
|
blogCategories?: UIDLEcommerceCategory[]
|
|
49
|
+
/** The booking items' category taxonomy — see `bookingSettings.categories`. */
|
|
50
|
+
bookingCategories?: UIDLEcommerceCategory[]
|
|
32
51
|
/** Post content headings get ids and `#` links — see `blogSettings.headingAnchors`. */
|
|
33
52
|
blogHeadingAnchors?: boolean
|
|
34
53
|
/**
|
|
@@ -77,10 +96,55 @@ const resolveAllowBackorders = (
|
|
|
77
96
|
* tax-inclusive or untaxed stores. `allowBackorders` is the effective
|
|
78
97
|
* "stock never blocks a purchase" flag — see `resolveAllowBackorders`.
|
|
79
98
|
*/
|
|
99
|
+
const contentPresetTransformSettings = (
|
|
100
|
+
settings: UIDLBlogSettings | undefined
|
|
101
|
+
): ContentPresetTransformSettings => ({
|
|
102
|
+
categories: settings?.categories,
|
|
103
|
+
headingAnchors: settings?.headingAnchors === true,
|
|
104
|
+
...(settings?.contents === true && { contents: true }),
|
|
105
|
+
comments: settings?.comments === true,
|
|
106
|
+
...(settings?.order === 'manual' && { order: 'manual' as const }),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The settings the transform of `tableName` bakes in: its preset's entry in
|
|
111
|
+
* `contentSettings`, or — for the Blog, and for a caller that only set the
|
|
112
|
+
* `blog*` fields — those fields. Empty for a table no preset owns.
|
|
113
|
+
*/
|
|
114
|
+
export const contentSettingsFor = (
|
|
115
|
+
options: Pick<
|
|
116
|
+
EntityTransformOptions,
|
|
117
|
+
'contentSettings' | 'blogCategories' | 'blogHeadingAnchors' | 'blogComments'
|
|
118
|
+
>,
|
|
119
|
+
tableName: string
|
|
120
|
+
): ContentPresetTransformSettings => {
|
|
121
|
+
const role = ContentTables.contentTableRole(stripSchemaQualifier(tableName))
|
|
122
|
+
if (!role) {
|
|
123
|
+
return {}
|
|
124
|
+
}
|
|
125
|
+
const own = options.contentSettings?.[role.key]
|
|
126
|
+
if (own) {
|
|
127
|
+
return own
|
|
128
|
+
}
|
|
129
|
+
if (role.key === 'blog') {
|
|
130
|
+
return {
|
|
131
|
+
categories: options.blogCategories,
|
|
132
|
+
headingAnchors: options.blogHeadingAnchors,
|
|
133
|
+
comments: options.blogComments,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {}
|
|
137
|
+
}
|
|
138
|
+
|
|
80
139
|
export const buildProductTransformOptions = (
|
|
81
140
|
options: Pick<
|
|
82
141
|
GeneratorOptions,
|
|
83
|
-
|
|
142
|
+
| 'ecommerceSettings'
|
|
143
|
+
| 'invoiceSettings'
|
|
144
|
+
| 'blogSettings'
|
|
145
|
+
| 'bookingSettings'
|
|
146
|
+
| 'helpCenterSettings'
|
|
147
|
+
| 'auth'
|
|
84
148
|
> &
|
|
85
149
|
LocalizedProjectOptions & {
|
|
86
150
|
/**
|
|
@@ -96,7 +160,12 @@ export const buildProductTransformOptions = (
|
|
|
96
160
|
}
|
|
97
161
|
): EntityTransformOptions => ({
|
|
98
162
|
categories: options.ecommerceSettings?.categories,
|
|
163
|
+
contentSettings: {
|
|
164
|
+
blog: contentPresetTransformSettings(options.blogSettings),
|
|
165
|
+
help: contentPresetTransformSettings(options.helpCenterSettings),
|
|
166
|
+
},
|
|
99
167
|
blogCategories: options.blogSettings?.categories,
|
|
168
|
+
bookingCategories: options.bookingSettings?.categories,
|
|
100
169
|
blogHeadingAnchors: options.blogSettings?.headingAnchors === true,
|
|
101
170
|
blogComments: options.blogSettings?.comments === true,
|
|
102
171
|
storefrontTaxRate: StorefrontTax.resolveStorefrontTaxRate(options.invoiceSettings),
|
|
@@ -106,7 +175,12 @@ export const buildProductTransformOptions = (
|
|
|
106
175
|
trustedReaderRoles: TableAccess.resolveTrustedReaderRoles(options.auth || options.authentication),
|
|
107
176
|
})
|
|
108
177
|
|
|
109
|
-
export type TransformationType =
|
|
178
|
+
export type TransformationType =
|
|
179
|
+
| 'blog-post'
|
|
180
|
+
| 'ecommerce-product'
|
|
181
|
+
| 'custom-page'
|
|
182
|
+
| 'booking-item'
|
|
183
|
+
| null
|
|
110
184
|
|
|
111
185
|
/**
|
|
112
186
|
* Strips a leading `schema.` qualifier from a table name so that only the
|
|
@@ -125,9 +199,9 @@ const stripSchemaQualifier = (tableName: string): string => {
|
|
|
125
199
|
* Detects which transformation type to apply based on the table name.
|
|
126
200
|
* Returns null if no transformation is needed.
|
|
127
201
|
*
|
|
128
|
-
* IMPORTANT: Only the real platform-managed tables (`teleport_products`,
|
|
129
|
-
*
|
|
130
|
-
* transforms. A previous loose substring match (`lower.includes('products')`)
|
|
202
|
+
* IMPORTANT: Only the real platform-managed tables (`teleport_products`, the
|
|
203
|
+
* content presets' posts tables) are routed through the e-commerce/blog
|
|
204
|
+
* view-model transforms. A previous loose substring match (`lower.includes('products')`)
|
|
131
205
|
* incorrectly routed CUSTOM tables such as `products`, `store_products` or
|
|
132
206
|
* `wholesale_products` through `buildEcommerceProduct`, which emits a fixed set
|
|
133
207
|
* of platform product fields and silently drops all custom columns. Matching the
|
|
@@ -138,7 +212,7 @@ export const detectTransformationType = (tableName: string): TransformationType
|
|
|
138
212
|
return null
|
|
139
213
|
}
|
|
140
214
|
const bare = stripSchemaQualifier(tableName)
|
|
141
|
-
if (bare
|
|
215
|
+
if (ContentTables.isContentPostsTable(bare)) {
|
|
142
216
|
return 'blog-post'
|
|
143
217
|
}
|
|
144
218
|
if (bare === 'teleport_products') {
|
|
@@ -147,6 +221,9 @@ export const detectTransformationType = (tableName: string): TransformationType
|
|
|
147
221
|
if (bare === 'teleport_pages') {
|
|
148
222
|
return 'custom-page'
|
|
149
223
|
}
|
|
224
|
+
if (bare === 'teleport_booking_items') {
|
|
225
|
+
return 'booking-item'
|
|
226
|
+
}
|
|
150
227
|
return null
|
|
151
228
|
}
|
|
152
229
|
|
|
@@ -167,18 +244,28 @@ export const getTransformationCode = (
|
|
|
167
244
|
const shared = generateSharedTransformationCode()
|
|
168
245
|
|
|
169
246
|
switch (type) {
|
|
170
|
-
case 'blog-post':
|
|
247
|
+
case 'blog-post': {
|
|
248
|
+
const role = ContentTables.contentTableRole(stripSchemaQualifier(tableName))
|
|
249
|
+
const settings = contentSettingsFor(options, tableName)
|
|
171
250
|
return (
|
|
172
251
|
shared +
|
|
173
252
|
generateBlogPostTransformationCode({
|
|
174
|
-
|
|
175
|
-
|
|
253
|
+
tables: role ? ContentTables.contentTablesByKey(role.key) : undefined,
|
|
254
|
+
categories: settings.categories,
|
|
255
|
+
headingAnchors: settings.headingAnchors,
|
|
256
|
+
contents: settings.contents === true,
|
|
257
|
+
arranged: settings.order === 'manual',
|
|
176
258
|
})
|
|
177
259
|
)
|
|
260
|
+
}
|
|
178
261
|
case 'ecommerce-product':
|
|
179
262
|
return shared + generateEcommerceProductTransformationCode(options)
|
|
180
263
|
case 'custom-page':
|
|
181
264
|
return shared + generateCustomPageTransformationCode()
|
|
265
|
+
case 'booking-item':
|
|
266
|
+
return (
|
|
267
|
+
shared + generateBookingItemTransformationCode({ categories: options.bookingCategories })
|
|
268
|
+
)
|
|
182
269
|
default:
|
|
183
270
|
return ''
|
|
184
271
|
}
|
|
@@ -300,7 +387,7 @@ export const REVIEWS_PER_PRODUCT = 5
|
|
|
300
387
|
*/
|
|
301
388
|
export const getTransformWrapperCode = (
|
|
302
389
|
tableName: string,
|
|
303
|
-
options: Pick<EntityTransformOptions, 'localization' | 'blogComments'> = {}
|
|
390
|
+
options: Pick<EntityTransformOptions, 'localization' | 'blogComments' | 'contentSettings'> = {}
|
|
304
391
|
): string => {
|
|
305
392
|
const type = detectTransformationType(tableName)
|
|
306
393
|
if (!type) {
|
|
@@ -314,6 +401,8 @@ export const getTransformWrapperCode = (
|
|
|
314
401
|
? 'transformBlogPosts'
|
|
315
402
|
: type === 'custom-page'
|
|
316
403
|
? 'transformCustomPages'
|
|
404
|
+
: type === 'booking-item'
|
|
405
|
+
? 'transformBookingItems'
|
|
317
406
|
: 'transformEcommerceProducts'
|
|
318
407
|
|
|
319
408
|
// Products additionally get their purchasable variant combinations attached in
|
|
@@ -422,7 +511,8 @@ export const getTransformWrapperCode = (
|
|
|
422
511
|
|
|
423
512
|
// A post page's neighbours and comments, on the same single-record heuristic
|
|
424
513
|
// as the related rail. The comments are only asked for when the blog takes
|
|
425
|
-
// them — see `EntityTransformOptions.blogComments`.
|
|
514
|
+
// them — see `EntityTransformOptions.blogComments`. `details` tells the
|
|
515
|
+
// transform the same thing: what only a post page draws (its Contents list).
|
|
426
516
|
const blogPageEnrichment =
|
|
427
517
|
type === 'blog-post'
|
|
428
518
|
? `
|
|
@@ -434,7 +524,7 @@ export const getTransformWrapperCode = (
|
|
|
434
524
|
} catch (e) {
|
|
435
525
|
// Best-effort; the post navigation stays hidden.
|
|
436
526
|
}${
|
|
437
|
-
options.
|
|
527
|
+
contentSettingsFor(options, tableName).comments === true
|
|
438
528
|
? `
|
|
439
529
|
try {
|
|
440
530
|
commentsByPostId = await getBlogCommentsMap(getClientFn, records)
|
|
@@ -447,7 +537,7 @@ export const getTransformWrapperCode = (
|
|
|
447
537
|
: ''
|
|
448
538
|
const blogPageOption =
|
|
449
539
|
type === 'blog-post'
|
|
450
|
-
? ', adjacentPostsById: adjacentPostsById, commentsByPostId: commentsByPostId'
|
|
540
|
+
? ', adjacentPostsById: adjacentPostsById, commentsByPostId: commentsByPostId, details: Array.isArray(records) && records.length === 1'
|
|
451
541
|
: ''
|
|
452
542
|
|
|
453
543
|
return `${RAW_ROWS_CODE}
|