@financial-times/cp-content-pipeline-schema 2.2.2 → 2.3.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/CHANGELOG.md +8 -0
- package/__mocks__/worker_threads.ts +3 -0
- package/lib/fixtures/dummyContext.js +4 -0
- package/lib/fixtures/dummyContext.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js.map +1 -1
- package/lib/model/CapiResponse.js +14 -6
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/Concept.test.js +9 -0
- package/lib/model/Concept.test.js.map +1 -1
- package/lib/model/RichText.d.ts +1 -1
- package/lib/model/RichText.js +34 -20
- package/lib/model/RichText.js.map +1 -1
- package/lib/model/RichText.test.js +9 -0
- package/lib/model/RichText.test.js.map +1 -1
- package/lib/resolvers/content-tree/bodyXMLToTree.d.ts +3 -4
- package/lib/resolvers/content-tree/bodyXMLToTree.js +2 -2
- package/lib/resolvers/content-tree/bodyXMLToTree.js.map +1 -1
- package/lib/resolvers/content-tree/bodyXMLToTree.test.js +32 -20
- package/lib/resolvers/content-tree/bodyXMLToTree.test.js.map +1 -1
- package/lib/resolvers/content-tree/bodyXMLToTreeWorker.d.ts +9 -0
- package/lib/resolvers/content-tree/bodyXMLToTreeWorker.js +18 -0
- package/lib/resolvers/content-tree/bodyXMLToTreeWorker.js.map +1 -0
- package/lib/resolvers/content-tree/nodePredicates.d.ts +13 -6
- package/lib/resolvers/content-tree/nodePredicates.js +33 -33
- package/lib/resolvers/content-tree/nodePredicates.js.map +1 -1
- package/lib/resolvers/content-tree/references/Reference.d.ts +1 -1
- package/lib/resolvers/content-tree/tagMappings.d.ts +1 -2
- package/lib/resolvers/content-tree/tagMappings.js +60 -60
- package/lib/resolvers/content-tree/tagMappings.js.map +1 -1
- package/package.json +2 -1
- package/src/fixtures/dummyContext.ts +4 -0
- package/src/index.ts +1 -0
- package/src/model/CapiResponse.ts +20 -10
- package/src/model/Concept.test.ts +9 -0
- package/src/model/RichText.test.ts +10 -0
- package/src/model/RichText.ts +39 -27
- package/src/resolvers/content-tree/bodyXMLToTree.test.ts +32 -22
- package/src/resolvers/content-tree/bodyXMLToTree.ts +6 -11
- package/src/resolvers/content-tree/bodyXMLToTreeWorker.ts +31 -0
- package/src/resolvers/content-tree/nodePredicates.ts +45 -39
- package/src/resolvers/content-tree/tagMappings.ts +60 -102
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
|
|
20
20
|
import * as scrollytelling from '@financial-times/n-scrollytelling-image/server'
|
|
21
21
|
import { LiteralToPrimitiveDeep } from 'type-fest'
|
|
22
|
-
import { CapiResponse } from '../../model/CapiResponse'
|
|
23
22
|
|
|
24
23
|
const tableResponsiveStyleMap: Record<string, Table['responsiveStyle']> = {
|
|
25
24
|
stacked: 'flat',
|
|
@@ -82,10 +81,10 @@ const validScrollytellingOption = <
|
|
|
82
81
|
: false
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
const articleTagMappings = (
|
|
84
|
+
const articleTagMappings = (topperHasImage: boolean): TagMappings => ({
|
|
86
85
|
'body > ft-content[type="http://www.ft.com/ontology/content/ImageSet"]:first-child':
|
|
87
86
|
($el) => ({
|
|
88
|
-
type:
|
|
87
|
+
type: topperHasImage ? 'image-set' : 'main-image',
|
|
89
88
|
id: $el.attr('url') || '',
|
|
90
89
|
}),
|
|
91
90
|
'body > ft-content[type="http://www.ft.com/ontology/content/ImageSet"]:not(:first-child),:not(body) > ft-content[type="http://www.ft.com/ontology/content/ImageSet"]':
|
|
@@ -104,60 +103,55 @@ const liveBlogPostTagMappings: TagMappings = {
|
|
|
104
103
|
|
|
105
104
|
const commonTagMappings: TagMappings = {
|
|
106
105
|
body: ($el, traverse) => ({ type: 'body', version: 1, children: traverse() }),
|
|
107
|
-
'a:not([data-asset-type])': ($el, traverse
|
|
106
|
+
'a:not([data-asset-type])': ($el, traverse) => ({
|
|
108
107
|
type: 'link',
|
|
109
108
|
url: $el.attr('href') || '',
|
|
110
109
|
title: '',
|
|
111
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'link'
|
|
110
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'link'),
|
|
112
111
|
}),
|
|
113
|
-
strong: ($el, traverse
|
|
112
|
+
strong: ($el, traverse) => ({
|
|
114
113
|
type: 'strong',
|
|
115
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'strong'
|
|
114
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'strong'),
|
|
116
115
|
}),
|
|
117
|
-
em: ($el, traverse
|
|
116
|
+
em: ($el, traverse) => ({
|
|
118
117
|
type: 'emphasis',
|
|
119
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'emphasis'
|
|
118
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'emphasis'),
|
|
120
119
|
}),
|
|
121
|
-
blockquote: ($el, traverse
|
|
120
|
+
blockquote: ($el, traverse) => ({
|
|
122
121
|
type: 'blockquote',
|
|
123
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'blockquote'
|
|
122
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'blockquote'),
|
|
124
123
|
}),
|
|
125
124
|
// strip any (redundant) line breaks in between two paragraphs
|
|
126
125
|
'p + br': ($el) => ($el.next()[0]?.tagName === 'p' ? [] : { type: 'break' }),
|
|
127
126
|
br: () => ({ type: 'break' }),
|
|
128
127
|
hr: () => ({ type: 'thematic-break' }),
|
|
129
|
-
s: ($el, traverse
|
|
128
|
+
s: ($el, traverse) => ({
|
|
130
129
|
type: 'strikethrough',
|
|
131
|
-
children: childrenOfTypes(
|
|
132
|
-
phrasingTypes,
|
|
133
|
-
traverse(),
|
|
134
|
-
'strikethrough',
|
|
135
|
-
context
|
|
136
|
-
),
|
|
130
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'strikethrough'),
|
|
137
131
|
}),
|
|
138
|
-
ul: ($el, traverse
|
|
132
|
+
ul: ($el, traverse) => ({
|
|
139
133
|
type: 'list',
|
|
140
|
-
children: everyChildIsType('list-item', traverse(), 'list'
|
|
134
|
+
children: everyChildIsType('list-item', traverse(), 'list'),
|
|
141
135
|
ordered: false,
|
|
142
136
|
}),
|
|
143
|
-
ol: ($el, traverse
|
|
137
|
+
ol: ($el, traverse) => ({
|
|
144
138
|
type: 'list',
|
|
145
|
-
children: everyChildIsType('list-item', traverse(), 'list'
|
|
139
|
+
children: everyChildIsType('list-item', traverse(), 'list'),
|
|
146
140
|
ordered: true,
|
|
147
141
|
}),
|
|
148
|
-
li: ($el, traverse
|
|
142
|
+
li: ($el, traverse) => ({
|
|
149
143
|
type: 'list-item',
|
|
150
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'list-item'
|
|
144
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'list-item'),
|
|
151
145
|
}),
|
|
152
146
|
'a[data-asset-type="tweet"]': ($el) => ({
|
|
153
147
|
type: 'tweet',
|
|
154
148
|
id: $el.attr('href') || '',
|
|
155
149
|
}),
|
|
156
150
|
|
|
157
|
-
'h4,h5,h6': ($el, traverse
|
|
151
|
+
'h4,h5,h6': ($el, traverse) => ({
|
|
158
152
|
type: 'heading',
|
|
159
153
|
level: 'label',
|
|
160
|
-
children: everyChildIsType('text', traverse(), 'heading'
|
|
154
|
+
children: everyChildIsType('text', traverse(), 'heading'),
|
|
161
155
|
}),
|
|
162
156
|
'pull-quote': ($el) => ({
|
|
163
157
|
type: 'pullquote',
|
|
@@ -236,7 +230,7 @@ const commonTagMappings: TagMappings = {
|
|
|
236
230
|
description: $description.text(),
|
|
237
231
|
}
|
|
238
232
|
},
|
|
239
|
-
'.n-content-layout': ($el, traverse
|
|
233
|
+
'.n-content-layout': ($el, traverse) => {
|
|
240
234
|
//TODO: this is a bit gross??
|
|
241
235
|
const isValidWidth = (
|
|
242
236
|
str: string
|
|
@@ -260,16 +254,14 @@ const commonTagMappings: TagMappings = {
|
|
|
260
254
|
...everyChildIsType<ContentTree.LayoutSlot>(
|
|
261
255
|
'layout-slot',
|
|
262
256
|
otherChildren,
|
|
263
|
-
'layout'
|
|
264
|
-
context
|
|
257
|
+
'layout'
|
|
265
258
|
),
|
|
266
259
|
]
|
|
267
260
|
} else {
|
|
268
261
|
return everyChildIsType<ContentTree.LayoutSlot>(
|
|
269
262
|
'layout-slot',
|
|
270
263
|
children,
|
|
271
|
-
'layout'
|
|
272
|
-
context
|
|
264
|
+
'layout'
|
|
273
265
|
)
|
|
274
266
|
}
|
|
275
267
|
}
|
|
@@ -281,7 +273,7 @@ const commonTagMappings: TagMappings = {
|
|
|
281
273
|
children: getChildren(traverse()),
|
|
282
274
|
}
|
|
283
275
|
},
|
|
284
|
-
'.n-content-layout__slot': ($el, traverse
|
|
276
|
+
'.n-content-layout__slot': ($el, traverse) => ({
|
|
285
277
|
type: 'layout-slot',
|
|
286
278
|
children: childrenOfTypes(
|
|
287
279
|
['heading', 'paragraph', 'layout-image'],
|
|
@@ -291,8 +283,7 @@ const commonTagMappings: TagMappings = {
|
|
|
291
283
|
.flatMap((node) =>
|
|
292
284
|
node.type === 'layout-slot' ? node.children : [node]
|
|
293
285
|
),
|
|
294
|
-
'layout-slot'
|
|
295
|
-
context
|
|
286
|
+
'layout-slot'
|
|
296
287
|
),
|
|
297
288
|
}),
|
|
298
289
|
'.n-content-layout img': ($el) => ({
|
|
@@ -303,7 +294,7 @@ const commonTagMappings: TagMappings = {
|
|
|
303
294
|
credit: $el.attr('data-copyright') || '',
|
|
304
295
|
}),
|
|
305
296
|
|
|
306
|
-
table: ($el, traverse
|
|
297
|
+
table: ($el, traverse): Table | ContentTree.Layout => {
|
|
307
298
|
const layoutSmallscreen = $el.attr('data-table-layout-smallscreen')
|
|
308
299
|
const responsiveStyle: Table['responsiveStyle'] =
|
|
309
300
|
layoutSmallscreen && layoutSmallscreen in tableResponsiveStyleMap
|
|
@@ -318,14 +309,12 @@ const commonTagMappings: TagMappings = {
|
|
|
318
309
|
const caption = findChildOftype<TableCaption>(
|
|
319
310
|
'table-caption',
|
|
320
311
|
children,
|
|
321
|
-
'table'
|
|
322
|
-
context
|
|
312
|
+
'table'
|
|
323
313
|
)
|
|
324
314
|
const footer = findChildOftype<TableFooter>(
|
|
325
315
|
'table-footer',
|
|
326
316
|
children,
|
|
327
|
-
'table'
|
|
328
|
-
context
|
|
317
|
+
'table'
|
|
329
318
|
)
|
|
330
319
|
|
|
331
320
|
// HACK:KB:20230523 transform single-cell tables into infoboxes, for legacy articles
|
|
@@ -339,8 +328,7 @@ const commonTagMappings: TagMappings = {
|
|
|
339
328
|
children: childrenOfTypes(
|
|
340
329
|
['paragraph', 'heading'],
|
|
341
330
|
bodies[0].children[0].children[0].children,
|
|
342
|
-
'layout-slot'
|
|
343
|
-
context
|
|
331
|
+
'layout-slot'
|
|
344
332
|
),
|
|
345
333
|
}
|
|
346
334
|
|
|
@@ -349,12 +337,7 @@ const commonTagMappings: TagMappings = {
|
|
|
349
337
|
{
|
|
350
338
|
type: 'heading',
|
|
351
339
|
level: 'subheading',
|
|
352
|
-
children: everyChildIsType(
|
|
353
|
-
'text',
|
|
354
|
-
caption.children,
|
|
355
|
-
'heading',
|
|
356
|
-
context
|
|
357
|
-
),
|
|
340
|
+
children: everyChildIsType('text', caption.children, 'heading'),
|
|
358
341
|
},
|
|
359
342
|
slot,
|
|
360
343
|
]
|
|
@@ -434,44 +417,34 @@ const commonTagMappings: TagMappings = {
|
|
|
434
417
|
// HACK needs to come before tr. TODO sort by selector specificity
|
|
435
418
|
'tfoot > tr': ($el, traverse) => traverse(),
|
|
436
419
|
// HACK needs to come before td. TODO sort by selector specificity
|
|
437
|
-
'tfoot > tr > td': ($el, traverse
|
|
420
|
+
'tfoot > tr > td': ($el, traverse) => ({
|
|
438
421
|
type: 'table-footer',
|
|
439
|
-
children: childrenOfTypes(
|
|
440
|
-
phrasingTypes,
|
|
441
|
-
traverse(),
|
|
442
|
-
'table-footer',
|
|
443
|
-
context
|
|
444
|
-
),
|
|
422
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'table-footer'),
|
|
445
423
|
}),
|
|
446
|
-
tr: ($el, traverse
|
|
424
|
+
tr: ($el, traverse) => ({
|
|
447
425
|
type: 'table-row',
|
|
448
|
-
children: everyChildIsType('table-cell', traverse(), 'table-row'
|
|
426
|
+
children: everyChildIsType('table-cell', traverse(), 'table-row'),
|
|
449
427
|
}),
|
|
450
|
-
td: ($el, traverse
|
|
428
|
+
td: ($el, traverse) => ({
|
|
451
429
|
type: 'table-cell',
|
|
452
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'
|
|
430
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'),
|
|
453
431
|
}),
|
|
454
|
-
th: ($el, traverse
|
|
432
|
+
th: ($el, traverse) => ({
|
|
455
433
|
type: 'table-cell',
|
|
456
434
|
heading: true,
|
|
457
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'
|
|
435
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'),
|
|
458
436
|
}),
|
|
459
|
-
thead: ($el, traverse
|
|
437
|
+
thead: ($el, traverse) => ({
|
|
460
438
|
type: 'table-body',
|
|
461
|
-
children: everyChildIsType('table-row', traverse(), 'table-body'
|
|
439
|
+
children: everyChildIsType('table-row', traverse(), 'table-body'),
|
|
462
440
|
}),
|
|
463
|
-
tbody: ($el, traverse
|
|
441
|
+
tbody: ($el, traverse) => ({
|
|
464
442
|
type: 'table-body',
|
|
465
|
-
children: everyChildIsType('table-row', traverse(), 'table-body'
|
|
443
|
+
children: everyChildIsType('table-row', traverse(), 'table-body'),
|
|
466
444
|
}),
|
|
467
|
-
caption: ($el, traverse
|
|
445
|
+
caption: ($el, traverse) => ({
|
|
468
446
|
type: 'table-caption',
|
|
469
|
-
children: childrenOfTypes(
|
|
470
|
-
phrasingTypes,
|
|
471
|
-
traverse(),
|
|
472
|
-
'table-caption',
|
|
473
|
-
context
|
|
474
|
-
),
|
|
447
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'table-caption'),
|
|
475
448
|
}),
|
|
476
449
|
|
|
477
450
|
// HACK needs to come after LayoutImage. TODO sort by selector specificity
|
|
@@ -495,15 +468,14 @@ const commonTagMappings: TagMappings = {
|
|
|
495
468
|
format: 'standard',
|
|
496
469
|
}),
|
|
497
470
|
|
|
498
|
-
'scrollable-block': ($el, traverse
|
|
471
|
+
'scrollable-block': ($el, traverse) => {
|
|
499
472
|
return {
|
|
500
473
|
type: 'scrolly-block',
|
|
501
474
|
theme: $el.attr('theme') === '2' ? 'serif' : 'sans',
|
|
502
475
|
children: everyChildIsType(
|
|
503
476
|
'scrolly-section',
|
|
504
477
|
traverse(),
|
|
505
|
-
'scrolly-block'
|
|
506
|
-
context
|
|
478
|
+
'scrolly-block'
|
|
507
479
|
),
|
|
508
480
|
}
|
|
509
481
|
},
|
|
@@ -514,7 +486,7 @@ const commonTagMappings: TagMappings = {
|
|
|
514
486
|
id: $el.attr('url') ?? '',
|
|
515
487
|
}),
|
|
516
488
|
|
|
517
|
-
'scrollable-section': ($el, traverse
|
|
489
|
+
'scrollable-section': ($el, traverse) => {
|
|
518
490
|
const [firstChild, ...restChildren] = traverse()
|
|
519
491
|
|
|
520
492
|
const display = $el.attr('theme-display')
|
|
@@ -538,33 +510,29 @@ const commonTagMappings: TagMappings = {
|
|
|
538
510
|
...everyChildIsType<ContentTree.ScrollyImage>(
|
|
539
511
|
'scrolly-image',
|
|
540
512
|
[firstChild],
|
|
541
|
-
'scrolly-section'
|
|
542
|
-
context
|
|
513
|
+
'scrolly-section'
|
|
543
514
|
),
|
|
544
515
|
...everyChildIsType<ContentTree.ScrollyCopy>(
|
|
545
516
|
'scrolly-copy',
|
|
546
517
|
restChildren,
|
|
547
|
-
'scrolly-section'
|
|
548
|
-
context
|
|
518
|
+
'scrolly-section'
|
|
549
519
|
),
|
|
550
520
|
] as [ContentTree.ScrollyImage, ...ContentTree.ScrollyCopy[]],
|
|
551
521
|
}
|
|
552
522
|
},
|
|
553
523
|
|
|
554
|
-
'scrollable-text': ($el, traverse
|
|
524
|
+
'scrollable-text': ($el, traverse) => ({
|
|
555
525
|
type: 'scrolly-copy',
|
|
556
526
|
children: childrenOfTypes(
|
|
557
527
|
['scrolly-heading', 'paragraph'],
|
|
558
528
|
traverse(),
|
|
559
|
-
'scrolly-copy'
|
|
560
|
-
context
|
|
529
|
+
'scrolly-copy'
|
|
561
530
|
),
|
|
562
531
|
}),
|
|
563
532
|
|
|
564
533
|
'scrollable-text > p, scrollable-text > h1, scrollable-text > h2': (
|
|
565
534
|
$el,
|
|
566
|
-
traverse
|
|
567
|
-
context
|
|
535
|
+
traverse
|
|
568
536
|
) => {
|
|
569
537
|
const textStyle = $el.attr('theme-style')
|
|
570
538
|
|
|
@@ -572,41 +540,31 @@ const commonTagMappings: TagMappings = {
|
|
|
572
540
|
return {
|
|
573
541
|
type: 'scrolly-heading',
|
|
574
542
|
level: scrollytellingOptionsToContentTree.textStyle[textStyle],
|
|
575
|
-
children: everyChildIsType(
|
|
576
|
-
'text',
|
|
577
|
-
traverse(),
|
|
578
|
-
'scrolly-heading',
|
|
579
|
-
context
|
|
580
|
-
),
|
|
543
|
+
children: everyChildIsType('text', traverse(), 'scrolly-heading'),
|
|
581
544
|
}
|
|
582
545
|
}
|
|
583
546
|
|
|
584
547
|
return {
|
|
585
548
|
type: 'paragraph',
|
|
586
|
-
children: childrenOfTypes(
|
|
587
|
-
phrasingTypes,
|
|
588
|
-
traverse(),
|
|
589
|
-
'paragraph',
|
|
590
|
-
context
|
|
591
|
-
),
|
|
549
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'paragraph'),
|
|
592
550
|
}
|
|
593
551
|
},
|
|
594
552
|
|
|
595
553
|
// HACK:KB:20230703 needs to come after `scrollable-text > X`. todo sort by specificity
|
|
596
|
-
p: ($el, traverse
|
|
554
|
+
p: ($el, traverse) => ({
|
|
597
555
|
type: 'paragraph',
|
|
598
|
-
children: childrenOfTypes(phrasingTypes, traverse(), 'paragraph'
|
|
556
|
+
children: childrenOfTypes(phrasingTypes, traverse(), 'paragraph'),
|
|
599
557
|
}),
|
|
600
558
|
|
|
601
|
-
h1: ($el, traverse
|
|
559
|
+
h1: ($el, traverse) => ({
|
|
602
560
|
type: 'heading',
|
|
603
561
|
level: 'chapter',
|
|
604
|
-
children: everyChildIsType('text', traverse(), 'heading'
|
|
562
|
+
children: everyChildIsType('text', traverse(), 'heading'),
|
|
605
563
|
}),
|
|
606
|
-
'h2,h3': ($el, traverse
|
|
564
|
+
'h2,h3': ($el, traverse) => ({
|
|
607
565
|
type: 'heading',
|
|
608
566
|
level: 'subheading',
|
|
609
|
-
children: everyChildIsType('text', traverse(), 'heading'
|
|
567
|
+
children: everyChildIsType('text', traverse(), 'heading'),
|
|
610
568
|
}),
|
|
611
569
|
'ft-content[type="http://www.ft.com/ontology/content/ClipSet"]': ($el) => ({
|
|
612
570
|
type: 'clip-set',
|