@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/__mocks__/worker_threads.ts +3 -0
  3. package/lib/fixtures/dummyContext.js +4 -0
  4. package/lib/fixtures/dummyContext.js.map +1 -1
  5. package/lib/index.d.ts +1 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/model/CapiResponse.js +14 -6
  8. package/lib/model/CapiResponse.js.map +1 -1
  9. package/lib/model/Concept.test.js +9 -0
  10. package/lib/model/Concept.test.js.map +1 -1
  11. package/lib/model/RichText.d.ts +1 -1
  12. package/lib/model/RichText.js +34 -20
  13. package/lib/model/RichText.js.map +1 -1
  14. package/lib/model/RichText.test.js +9 -0
  15. package/lib/model/RichText.test.js.map +1 -1
  16. package/lib/resolvers/content-tree/bodyXMLToTree.d.ts +3 -4
  17. package/lib/resolvers/content-tree/bodyXMLToTree.js +2 -2
  18. package/lib/resolvers/content-tree/bodyXMLToTree.js.map +1 -1
  19. package/lib/resolvers/content-tree/bodyXMLToTree.test.js +32 -20
  20. package/lib/resolvers/content-tree/bodyXMLToTree.test.js.map +1 -1
  21. package/lib/resolvers/content-tree/bodyXMLToTreeWorker.d.ts +9 -0
  22. package/lib/resolvers/content-tree/bodyXMLToTreeWorker.js +18 -0
  23. package/lib/resolvers/content-tree/bodyXMLToTreeWorker.js.map +1 -0
  24. package/lib/resolvers/content-tree/nodePredicates.d.ts +13 -6
  25. package/lib/resolvers/content-tree/nodePredicates.js +33 -33
  26. package/lib/resolvers/content-tree/nodePredicates.js.map +1 -1
  27. package/lib/resolvers/content-tree/references/Reference.d.ts +1 -1
  28. package/lib/resolvers/content-tree/tagMappings.d.ts +1 -2
  29. package/lib/resolvers/content-tree/tagMappings.js +60 -60
  30. package/lib/resolvers/content-tree/tagMappings.js.map +1 -1
  31. package/package.json +2 -1
  32. package/src/fixtures/dummyContext.ts +4 -0
  33. package/src/index.ts +1 -0
  34. package/src/model/CapiResponse.ts +20 -10
  35. package/src/model/Concept.test.ts +9 -0
  36. package/src/model/RichText.test.ts +10 -0
  37. package/src/model/RichText.ts +39 -27
  38. package/src/resolvers/content-tree/bodyXMLToTree.test.ts +32 -22
  39. package/src/resolvers/content-tree/bodyXMLToTree.ts +6 -11
  40. package/src/resolvers/content-tree/bodyXMLToTreeWorker.ts +31 -0
  41. package/src/resolvers/content-tree/nodePredicates.ts +45 -39
  42. package/src/resolvers/content-tree/tagMappings.ts +60 -102
  43. 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 = (capiData?: CapiResponse): TagMappings => ({
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: capiData?.topperHasImage() ? 'image-set' : 'main-image',
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, context) => ({
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', context),
110
+ children: childrenOfTypes(phrasingTypes, traverse(), 'link'),
112
111
  }),
113
- strong: ($el, traverse, context) => ({
112
+ strong: ($el, traverse) => ({
114
113
  type: 'strong',
115
- children: childrenOfTypes(phrasingTypes, traverse(), 'strong', context),
114
+ children: childrenOfTypes(phrasingTypes, traverse(), 'strong'),
116
115
  }),
117
- em: ($el, traverse, context) => ({
116
+ em: ($el, traverse) => ({
118
117
  type: 'emphasis',
119
- children: childrenOfTypes(phrasingTypes, traverse(), 'emphasis', context),
118
+ children: childrenOfTypes(phrasingTypes, traverse(), 'emphasis'),
120
119
  }),
121
- blockquote: ($el, traverse, context) => ({
120
+ blockquote: ($el, traverse) => ({
122
121
  type: 'blockquote',
123
- children: childrenOfTypes(phrasingTypes, traverse(), 'blockquote', context),
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, context) => ({
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, context) => ({
132
+ ul: ($el, traverse) => ({
139
133
  type: 'list',
140
- children: everyChildIsType('list-item', traverse(), 'list', context),
134
+ children: everyChildIsType('list-item', traverse(), 'list'),
141
135
  ordered: false,
142
136
  }),
143
- ol: ($el, traverse, context) => ({
137
+ ol: ($el, traverse) => ({
144
138
  type: 'list',
145
- children: everyChildIsType('list-item', traverse(), 'list', context),
139
+ children: everyChildIsType('list-item', traverse(), 'list'),
146
140
  ordered: true,
147
141
  }),
148
- li: ($el, traverse, context) => ({
142
+ li: ($el, traverse) => ({
149
143
  type: 'list-item',
150
- children: childrenOfTypes(phrasingTypes, traverse(), 'list-item', context),
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, context) => ({
151
+ 'h4,h5,h6': ($el, traverse) => ({
158
152
  type: 'heading',
159
153
  level: 'label',
160
- children: everyChildIsType('text', traverse(), 'heading', context),
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, context) => {
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, context) => ({
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, context): Table | ContentTree.Layout => {
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, context) => ({
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, context) => ({
424
+ tr: ($el, traverse) => ({
447
425
  type: 'table-row',
448
- children: everyChildIsType('table-cell', traverse(), 'table-row', context),
426
+ children: everyChildIsType('table-cell', traverse(), 'table-row'),
449
427
  }),
450
- td: ($el, traverse, context) => ({
428
+ td: ($el, traverse) => ({
451
429
  type: 'table-cell',
452
- children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell', context),
430
+ children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'),
453
431
  }),
454
- th: ($el, traverse, context) => ({
432
+ th: ($el, traverse) => ({
455
433
  type: 'table-cell',
456
434
  heading: true,
457
- children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell', context),
435
+ children: childrenOfTypes(phrasingTypes, traverse(), 'table-cell'),
458
436
  }),
459
- thead: ($el, traverse, context) => ({
437
+ thead: ($el, traverse) => ({
460
438
  type: 'table-body',
461
- children: everyChildIsType('table-row', traverse(), 'table-body', context),
439
+ children: everyChildIsType('table-row', traverse(), 'table-body'),
462
440
  }),
463
- tbody: ($el, traverse, context) => ({
441
+ tbody: ($el, traverse) => ({
464
442
  type: 'table-body',
465
- children: everyChildIsType('table-row', traverse(), 'table-body', context),
443
+ children: everyChildIsType('table-row', traverse(), 'table-body'),
466
444
  }),
467
- caption: ($el, traverse, context) => ({
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, context) => {
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, context) => {
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, context) => ({
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, context) => ({
554
+ p: ($el, traverse) => ({
597
555
  type: 'paragraph',
598
- children: childrenOfTypes(phrasingTypes, traverse(), 'paragraph', context),
556
+ children: childrenOfTypes(phrasingTypes, traverse(), 'paragraph'),
599
557
  }),
600
558
 
601
- h1: ($el, traverse, context) => ({
559
+ h1: ($el, traverse) => ({
602
560
  type: 'heading',
603
561
  level: 'chapter',
604
- children: everyChildIsType('text', traverse(), 'heading', context),
562
+ children: everyChildIsType('text', traverse(), 'heading'),
605
563
  }),
606
- 'h2,h3': ($el, traverse, context) => ({
564
+ 'h2,h3': ($el, traverse) => ({
607
565
  type: 'heading',
608
566
  level: 'subheading',
609
- children: everyChildIsType('text', traverse(), 'heading', context),
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',