@financial-times/cp-content-pipeline-schema 0.3.1 → 0.3.2

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.2](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v0.3.1...cp-content-pipeline-schema-v0.3.2) (2022-10-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * add simple html formatting tags ([7f5b229](https://github.com/Financial-Times/cp-content-pipeline/commit/7f5b229162d92f687ea16e1b4a34883179e337da))
9
+ * add tag class for Tweets and basic blockquote ([e5f525a](https://github.com/Financial-Times/cp-content-pipeline/commit/e5f525ac3c89128332852a79e1a580505f1741d0))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * handle bylines without any author data ([5458a1a](https://github.com/Financial-Times/cp-content-pipeline/commit/5458a1ac35749c1503d19ab8773ded9791fd214f))
15
+ * handle errors from twitter api gracefully ([2d285b3](https://github.com/Financial-Times/cp-content-pipeline/commit/2d285b30b1e8a0c7c3c344c97b5a7cbe10a3d304))
16
+
3
17
  ## [0.3.1](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v0.3.0...cp-content-pipeline-schema-v0.3.1) (2022-10-11)
4
18
 
5
19
 
@@ -1,9 +1,11 @@
1
1
  import { CapiDataSource } from './capi.js';
2
2
  import { OrigamiImageDataSource } from './origami-image.js';
3
+ import { TwitterDataSource } from './twitter.js';
3
4
  import { URLManagementDataSource } from './url-management.js';
4
5
  export declare const dataSources: () => {
5
6
  capi: CapiDataSource;
6
7
  origami: OrigamiImageDataSource;
7
8
  vanityUrls: URLManagementDataSource;
9
+ twitter: TwitterDataSource;
8
10
  };
9
11
  export declare type DataSources = ReturnType<typeof dataSources>;
@@ -1,9 +1,11 @@
1
1
  import { CapiDataSource } from './capi.js';
2
2
  import { OrigamiImageDataSource } from './origami-image.js';
3
+ import { TwitterDataSource } from './twitter.js';
3
4
  import { URLManagementDataSource } from './url-management.js';
4
5
  export const dataSources = () => ({
5
6
  capi: new CapiDataSource(),
6
7
  origami: new OrigamiImageDataSource(),
7
8
  vanityUrls: new URLManagementDataSource(),
9
+ twitter: new TwitterDataSource(),
8
10
  });
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datasources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAE7D,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,CAAC;IAChC,IAAI,EAAE,IAAI,cAAc,EAAE;IAC1B,OAAO,EAAE,IAAI,sBAAsB,EAAE;IACrC,UAAU,EAAE,IAAI,uBAAuB,EAAE;CAC1C,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datasources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAE7D,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,CAAC;IAChC,IAAI,EAAE,IAAI,cAAc,EAAE;IAC1B,OAAO,EAAE,IAAI,sBAAsB,EAAE;IACrC,UAAU,EAAE,IAAI,uBAAuB,EAAE;IACzC,OAAO,EAAE,IAAI,iBAAiB,EAAE;CACjC,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { InstrumentedRESTDataSource } from './instrumented.js';
2
+ export declare class TwitterDataSource extends InstrumentedRESTDataSource {
3
+ baseURL: string;
4
+ getTweet(tweetUrl: string): Promise<any>;
5
+ }
@@ -0,0 +1,11 @@
1
+ import { InstrumentedRESTDataSource } from './instrumented.js';
2
+ export class TwitterDataSource extends InstrumentedRESTDataSource {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.baseURL = 'https://publish.twitter.com';
6
+ }
7
+ async getTweet(tweetUrl) {
8
+ return this.get(`/oembed?url=${tweetUrl}&omit_script=true`);
9
+ }
10
+ }
11
+ //# sourceMappingURL=twitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twitter.js","sourceRoot":"","sources":["../../src/datasources/twitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AAE9D,MAAM,OAAO,iBAAkB,SAAQ,0BAA0B;IAAjE;;QACE,YAAO,GAAG,6BAA6B,CAAA;IAKzC,CAAC;IAHC,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,QAAQ,mBAAmB,CAAC,CAAA;IAC7D,CAAC;CACF"}
package/lib/index.js CHANGED
@@ -9,7 +9,7 @@ import { typeDef as RichText, resolvers as richTextResolvers, } from './richText
9
9
  import { typeDef as Recommended, resolvers as recommendedResolvers, } from './teaser.js';
10
10
  import tags from './tags/index.js';
11
11
  const tagEntries = Object.entries(tags);
12
- const tagsWithReferences = tagEntries.filter(([_key, tag]) => tag.typeDef);
12
+ const tagsWithReferences = tagEntries.filter(([, tag]) => tag.typeDef);
13
13
  const tagTypeDefs = tagsWithReferences.flatMap(([_key, tag]) => tag.typeDef);
14
14
  const Reference = gql `union Reference = ${tagsWithReferences
15
15
  .flatMap(([key]) => key)
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEjC,OAAO,EAAE,WAAW,EAAe,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,YAAY,CAAA;AAC1E,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EACL,OAAO,IAAI,QAAQ,EACnB,SAAS,IAAI,iBAAiB,GAC/B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,OAAO,IAAI,WAAW,EACtB,SAAS,IAAI,oBAAoB,GAClC,MAAM,aAAa,CAAA;AACpB,OAAO,IAAI,MAAM,iBAAiB,CAAA;AAGlC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AACvC,MAAM,kBAAkB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AAC1E,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAC5C,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAuB,CAC7C,CAAA;AACD,MAAM,SAAS,GAAG,GAAG,CAAA,qBAAqB,kBAAkB;KACzD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;KACvB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAA;AAEhB,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CACrC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAC9E,CAAA;AAED,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;CAOvB,CAAA;AAQD,MAAM,CAAC,MAAM,SAAS,GAGlB;IACF,KAAK,EAAE;QACL,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO;YACjC,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvD,CAAC;QACD,eAAe,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE;YAC5B,OAAO,OAAO,CAAA;QAChB,CAAC;KACF;IAED,SAAS,EAAE;QACT,aAAa,CAAC,GAAsC;YAClD,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAA;QAC7B,CAAC;KACF;IAED,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;IACnB,GAAG,cAAc;IACjB,GAAG,YAAY;IACf,GAAG,eAAe;IAClB,GAAG,iBAAiB;IACpB,GAAG,oBAAoB;CACxB,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,OAAO;IACP,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,QAAQ;IACR,WAAW;IACX,SAAS;IACT,WAAW;IACX,YAAY;CACb,CAAA;AAED,OAAO,EAAE,WAAW,EAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEjC,OAAO,EAAE,WAAW,EAAe,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,YAAY,CAAA;AAC1E,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EACL,OAAO,IAAI,QAAQ,EACnB,SAAS,IAAI,iBAAiB,GAC/B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,OAAO,IAAI,WAAW,EACtB,SAAS,IAAI,oBAAoB,GAClC,MAAM,aAAa,CAAA;AACpB,OAAO,IAAI,MAAM,iBAAiB,CAAA;AAGlC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AACvC,MAAM,kBAAkB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AACtE,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAC5C,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAuB,CAC7C,CAAA;AACD,MAAM,SAAS,GAAG,GAAG,CAAA,qBAAqB,kBAAkB;KACzD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;KACvB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAA;AAEhB,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CACrC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAC9E,CAAA;AAED,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;CAOvB,CAAA;AAQD,MAAM,CAAC,MAAM,SAAS,GAGlB;IACF,KAAK,EAAE;QACL,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO;YACjC,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvD,CAAC;QACD,eAAe,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE;YAC5B,OAAO,OAAO,CAAA;QAChB,CAAC;KACF;IAED,SAAS,EAAE;QACT,aAAa,CAAC,GAAsC;YAClD,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAA;QAC7B,CAAC;KACF;IAED,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;IACnB,GAAG,cAAc;IACjB,GAAG,YAAY;IACf,GAAG,eAAe;IAClB,GAAG,iBAAiB;IACpB,GAAG,oBAAoB;CACxB,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,OAAO;IACP,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,QAAQ;IACR,WAAW;IACX,SAAS;IACT,WAAW;IACX,YAAY;CACb,CAAA;AAED,OAAO,EAAE,WAAW,EAAe,CAAA"}
@@ -18,6 +18,8 @@ export class Byline {
18
18
  }
19
19
  }
20
20
  _Byline_instances = new WeakSet(), _Byline_splitBylineByName = function _Byline_splitBylineByName(byline, names) {
21
+ if (!names.length)
22
+ return [byline];
21
23
  const regex = new RegExp(`(${names.join('|')})`, 'ig');
22
24
  return byline.split(regex).filter((string) => string !== '');
23
25
  }, _Byline_buildUrlTree = function _Byline_buildUrlTree(urlMapping, parts) {
@@ -1 +1 @@
1
- {"version":3,"file":"Byline.js","sourceRoot":"","sources":["../../src/model/Byline.ts"],"names":[],"mappings":";;;;;;AAAA,MAAM,OAAO,MAAM;IACjB,YAAoB,MAAc,EAAU,gBAAqB;QAA7C,WAAM,GAAN,MAAM,CAAQ;QAAU,qBAAgB,GAAhB,gBAAgB,CAAK;;IAAG,CAAC;IAErE,eAAe;QACb,MAAM,KAAK,GAAG,uBAAA,IAAI,oDAAmB,MAAvB,IAAI,EAAoB,IAAI,CAAC,MAAM,EAAE;YACjD,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE;SAChC,CAAC,CAAA;QACF,OAAO,uBAAA,IAAI,+CAAc,MAAlB,IAAI,EAAe,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACzD,CAAC;CA8BF;kGA5BoB,MAAc,EAAE,KAAe;IAChD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACtD,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,CAAA;AAC9D,CAAC,uDAEa,UAA+B,EAAE,KAAe;IAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAClC,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,GAAG,EAAE;YACP,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,YAAY;gBACrB,UAAU,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE;gBACzB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;aAC1C,CAAA;SACF;aAAM;YACL,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,IAAI;aACZ,CAAA;SACF;IACH,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;KACjC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"Byline.js","sourceRoot":"","sources":["../../src/model/Byline.ts"],"names":[],"mappings":";;;;;;AAAA,MAAM,OAAO,MAAM;IACjB,YAAoB,MAAc,EAAU,gBAAqB;QAA7C,WAAM,GAAN,MAAM,CAAQ;QAAU,qBAAgB,GAAhB,gBAAgB,CAAK;;IAAG,CAAC;IAErE,eAAe;QACb,MAAM,KAAK,GAAG,uBAAA,IAAI,oDAAmB,MAAvB,IAAI,EAAoB,IAAI,CAAC,MAAM,EAAE;YACjD,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE;SAChC,CAAC,CAAA;QACF,OAAO,uBAAA,IAAI,+CAAc,MAAlB,IAAI,EAAe,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACzD,CAAC;CA+BF;kGA7BoB,MAAc,EAAE,KAAe;IAChD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACtD,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,CAAA;AAC9D,CAAC,uDAEa,UAA+B,EAAE,KAAe;IAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAClC,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,GAAG,EAAE;YACP,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,YAAY;gBACrB,UAAU,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE;gBACzB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;aAC1C,CAAA;SACF;aAAM;YACL,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,IAAI;aACZ,CAAA;SACF;IACH,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;KACjC,CAAA;AACH,CAAC"}
@@ -56,5 +56,12 @@ describe('byline transformation', () => {
56
56
  const result = byline.buildBylineTree();
57
57
  expect(result).toMatchSnapshot();
58
58
  });
59
+ test('handles bylines without any matching authors', async () => {
60
+ const bylineText = 'Chris Giles and Nick Ramsbottom in London';
61
+ const authorUrlMapping = new Map([]);
62
+ const byline = new Byline(bylineText, authorUrlMapping);
63
+ const result = byline.buildBylineTree();
64
+ expect(result).toMatchSnapshot();
65
+ });
59
66
  });
60
67
  //# sourceMappingURL=Byline.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Byline.test.js","sourceRoot":"","sources":["../../src/model/Byline.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,UAAU,GAAG,uBAAuB,CAAA;QAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,UAAU,GAAG,uCAAuC,CAAA;QAC1D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;YACD;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,UAAU,GAAG,2CAA2C,CAAA;QAC9D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,UAAU,GAAG,uBAAuB,CAAA;QAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;YACD;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"Byline.test.js","sourceRoot":"","sources":["../../src/model/Byline.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,UAAU,GAAG,uBAAuB,CAAA;QAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,UAAU,GAAG,uCAAuC,CAAA;QAC1D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;YACD;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,UAAU,GAAG,2CAA2C,CAAA;QAC9D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,UAAU,GAAG,uBAAuB,CAAA;QAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;YAC/B;gBACE,aAAa;gBACb,qEAAqE;aACtE;YACD;gBACE,aAAa;gBACb,qEAAqE;aACtE;SACF,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,UAAU,GAAG,2CAA2C,CAAA;QAC9D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,CAAA;QAEpC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
package/lib/richText.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CapiResponse } from './model/CapiResponse.js';
2
- declare type RichTextResolverData = {
2
+ export declare type RichTextResolverData = {
3
3
  source: string;
4
4
  value: string;
5
5
  contentApiData?: CapiResponse;
@@ -11,4 +11,3 @@ export declare const resolvers: {
11
11
  structured(parent: RichTextResolverData): Promise<unknown>;
12
12
  };
13
13
  };
14
- export {};
package/lib/tags/Link.js CHANGED
@@ -11,7 +11,7 @@ export const Link = (_a = class Link {
11
11
  }
12
12
  }
13
13
  },
14
- _a.selector = 'a',
14
+ _a.selector = 'a:not([dataAssetType])',
15
15
  _a.typeDef = gql `
16
16
  type Link {
17
17
  href: String
@@ -1 +1 @@
1
- {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/tags/Link.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAIjC,MAAM,CAAC,MAAM,IAAI,SAAmB,MAAM,IAAI;QAK5C,YAAY,IAAkB;;YAC5B,IAAI,OAAO,CAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,0CAAE,IAAI,CAAA,KAAK,QAAQ,EAAE;gBAC9C,IAAI,CAAC,IAAI,GAAG,MAAA,IAAI,CAAC,UAAU,CAAC,IAAI,mCAAI,EAAE,CAAA;aACvC;iBAAM;gBACL,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;aACf;QACH,CAAC;KAOF;IAfQ,WAAQ,GAAG,GAAG;IAUd,UAAO,GAAG,GAAG,CAAA;;;;GAInB;OACF,CAAA"}
1
+ {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/tags/Link.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAIjC,MAAM,CAAC,MAAM,IAAI,SAAmB,MAAM,IAAI;QAK5C,YAAY,IAAkB;;YAC5B,IAAI,OAAO,CAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,0CAAE,IAAI,CAAA,KAAK,QAAQ,EAAE;gBAC9C,IAAI,CAAC,IAAI,GAAG,MAAA,IAAI,CAAC,UAAU,CAAC,IAAI,mCAAI,EAAE,CAAA;aACvC;iBAAM;gBACL,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;aACf;QACH,CAAC;KAOF;IAfQ,WAAQ,GAAG,wBAAwB;IAUnC,UAAO,GAAG,GAAG,CAAA;;;;GAInB;OACF,CAAA"}
@@ -0,0 +1,17 @@
1
+ import type { TagConstructor } from './index.js';
2
+ import type * as hast from 'hast';
3
+ import { QueryContext } from '../index.js';
4
+ import type { RichTextResolverData } from '../richText.js';
5
+ declare class Tweet {
6
+ tweetUrl: string;
7
+ static selector: string;
8
+ constructor(node: hast.Element);
9
+ embed(context: QueryContext): Promise<RichTextResolverData | null>;
10
+ static resolve: {
11
+ embed: (parent: Tweet, _args: any, context: QueryContext) => Promise<RichTextResolverData | null>;
12
+ tweetUrl: (parent: Tweet) => string;
13
+ };
14
+ static typeDef: import("graphql").DocumentNode;
15
+ }
16
+ declare const TweetClass: TagConstructor<Tweet>;
17
+ export { TweetClass as Tweet };
@@ -0,0 +1,40 @@
1
+ var _a;
2
+ import { gql } from 'graphql-tag';
3
+ class Tweet {
4
+ constructor(node) {
5
+ var _b;
6
+ if (((_b = node.properties) === null || _b === void 0 ? void 0 : _b.href) && typeof node.properties.href === 'string') {
7
+ this.tweetUrl = node.properties.href;
8
+ }
9
+ else {
10
+ throw new Error('Embedded tweet is missing a href');
11
+ }
12
+ }
13
+ async embed(context) {
14
+ try {
15
+ const tweet = await context.dataSources.twitter.getTweet(this.tweetUrl);
16
+ return {
17
+ source: 'twitter',
18
+ value: tweet.html,
19
+ };
20
+ }
21
+ catch (err) {
22
+ return null;
23
+ }
24
+ }
25
+ }
26
+ _a = Tweet;
27
+ Tweet.selector = 'a[dataAssetType="tweet"]';
28
+ Tweet.resolve = {
29
+ embed: async (parent, _args, context) => parent.embed(context),
30
+ tweetUrl: (parent) => parent.tweetUrl,
31
+ };
32
+ Tweet.typeDef = gql `
33
+ type Tweet {
34
+ tweetUrl: String
35
+ embed: RichText
36
+ }
37
+ `;
38
+ const TweetClass = Tweet;
39
+ export { TweetClass as Tweet };
40
+ //# sourceMappingURL=Tweet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tweet.js","sourceRoot":"","sources":["../../src/tags/Tweet.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAMjC,MAAM,KAAK;IAKT,YAAY,IAAkB;;QAC5B,IAAI,CAAA,MAAA,IAAI,CAAC,UAAU,0CAAE,IAAI,KAAI,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,QAAQ,EAAE;YACrE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAA;SACrC;aAAM;YACL,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;SACpD;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAqB;QAC/B,IAAI;YACF,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACvE,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;aAClB,CAAA;SACF;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,IAAI,CAAA;SACZ;IACH,CAAC;;;AApBM,cAAQ,GAAG,0BAA0B,CAAA;AAsBrC,aAAO,GAAG;IACf,KAAK,EAAE,KAAK,EAAE,MAAa,EAAE,KAAU,EAAE,OAAqB,EAAE,EAAE,CAChE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IACvB,QAAQ,EAAE,CAAC,MAAa,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ;CAC7C,CAAA;AAEM,aAAO,GAAG,GAAG,CAAA;;;;;GAKnB,CAAA;AAGH,MAAM,UAAU,GAA0B,KAAK,CAAA;AAE/C,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import { Tweet } from './Tweet.js';
2
+ import { select } from 'hast-util-select';
3
+ import { unified } from 'unified';
4
+ import rehypeParse from 'rehype-parse';
5
+ import { jest } from '@jest/globals';
6
+ async function createAST(input) {
7
+ const formatOutput = function formatOutput() {
8
+ this.Compiler = (tree) => tree;
9
+ };
10
+ const body = await unified()
11
+ .use(rehypeParse, { fragment: true })
12
+ .use([formatOutput])
13
+ .process(input);
14
+ return body.result;
15
+ }
16
+ describe('Embedded Tweets', () => {
17
+ let body;
18
+ const getTweetMock = jest.fn();
19
+ const context = {
20
+ dataSources: {
21
+ twitter: {
22
+ getTweet: getTweetMock,
23
+ },
24
+ },
25
+ };
26
+ beforeAll(async () => {
27
+ const input = `<body><a href="https://www.twitter.com/blah" data-asset-type="tweet">Tweet</a></body>`;
28
+ body = await createAST(input);
29
+ });
30
+ afterEach(() => {
31
+ jest.resetAllMocks();
32
+ });
33
+ it('correctly matches the content API tag', () => {
34
+ const match = select(Tweet.selector, body);
35
+ expect(match).toBeTruthy();
36
+ });
37
+ it('provides the tweetUrl', () => {
38
+ const node = select(Tweet.selector, body);
39
+ if (!node) {
40
+ throw new Error('Expected a tweet node to be found');
41
+ }
42
+ const tweet = new Tweet(node);
43
+ expect(tweet).toEqual({
44
+ tweetUrl: 'https://www.twitter.com/blah',
45
+ });
46
+ });
47
+ it('provides the twitter embed code', async () => {
48
+ const node = select(Tweet.selector, body);
49
+ if (!node) {
50
+ throw new Error('Expected a tweet node to be found');
51
+ }
52
+ const tweet = new Tweet(node);
53
+ getTweetMock.mockReturnValue({
54
+ html: '<blockquote>this is an embed</blockquote>',
55
+ });
56
+ expect(await tweet.embed(context)).toEqual({
57
+ source: 'twitter',
58
+ value: '<blockquote>this is an embed</blockquote>',
59
+ });
60
+ });
61
+ it('handles errors fetching the tweet embed', async () => {
62
+ const node = select(Tweet.selector, body);
63
+ if (!node) {
64
+ throw new Error('Expected a tweet node to be found');
65
+ }
66
+ const tweet = new Tweet(node);
67
+ getTweetMock.mockRejectedValue(new Error('404'));
68
+ expect(await tweet.embed(context)).toEqual(null);
69
+ });
70
+ });
71
+ //# sourceMappingURL=Tweet.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tweet.test.js","sourceRoot":"","sources":["../../src/tags/Tweet.test.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,8 @@ import type { IObjectTypeResolver } from '@graphql-tools/utils';
3
3
  import type { DocumentNode } from 'graphql';
4
4
  import type { CapiResponse } from '../model/CapiResponse.js';
5
5
  export declare type Tag = object;
6
- export interface TagConstructor {
7
- new (node: hast.Element, contentApiData?: CapiResponse): Tag;
6
+ export interface TagConstructor<T = Tag> {
7
+ new (node: hast.Element, contentApiData?: CapiResponse): T;
8
8
  selector: string;
9
9
  typeDef?: DocumentNode;
10
10
  resolve?: IObjectTypeResolver;
package/lib/tags/index.js CHANGED
@@ -3,6 +3,7 @@ import { LayoutImage } from './LayoutImage.js';
3
3
  import { ImageSet } from './ImageSet.js';
4
4
  import { PullQuote } from './PullQuote.js';
5
5
  import { Recommended } from './Recommended.js';
6
+ import { Tweet } from './Tweet.js';
6
7
  function createTagWithoutReference(name, selector) {
7
8
  var _a;
8
9
  const tag = (_a = class {
@@ -20,6 +21,7 @@ const mapping = {
20
21
  RecommendedTitle: createTagWithoutReference('RecommendedTitle', 'recommended-title'),
21
22
  Paragraph: createTagWithoutReference('Paragraph', 'p'),
22
23
  UnorderedList: createTagWithoutReference('UnorderedList', 'ul'),
24
+ OrderedList: createTagWithoutReference('OrderedList', 'ol'),
23
25
  ListItem: createTagWithoutReference('ListItem', 'li'),
24
26
  Layout: createTagWithoutReference('Layout', '.n-content-layout, .layout'),
25
27
  LayoutContainer: createTagWithoutReference('LayoutContainer', '.n-content-layout__container, .layout-container'),
@@ -28,6 +30,13 @@ const mapping = {
28
30
  PullQuote,
29
31
  PullQuoteText: createTagWithoutReference('PullQuoteText', 'pull-quote-text'),
30
32
  PullQuoteSource: createTagWithoutReference('PullQuoteText', 'pull-quote-source'),
33
+ Blockquote: createTagWithoutReference('Blockquote', 'blockquote'),
34
+ Tweet,
35
+ LineBreak: createTagWithoutReference('LineBreak', 'br'),
36
+ HorizontalRule: createTagWithoutReference('HorizontalRule', 'hr'),
37
+ Emphasis: createTagWithoutReference('Emphasis', 'em'),
38
+ Strong: createTagWithoutReference('Strong', 'strong'),
39
+ Strikethrough: createTagWithoutReference('Strikethrough', 's'),
31
40
  };
32
41
  export default mapping;
33
42
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tags/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAW9C,SAAS,yBAAyB,CAChC,IAAY,EACZ,QAAgB;;IAEhB,MAAM,GAAG,SAAG;SAEX;QADQ,WAAQ,GAAG,QAAQ;WAC3B,CAAA;IAED,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAEnD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,OAAO,GAAmC;IAC9C,IAAI;IACJ,YAAY,EAAE,yBAAyB,CAAC,cAAc,EAAE,cAAc,CAAC;IACvE,QAAQ;IACR,WAAW;IACX,gBAAgB,EAAE,yBAAyB,CACzC,kBAAkB,EAClB,mBAAmB,CACpB;IACD,SAAS,EAAE,yBAAyB,CAAC,WAAW,EAAE,GAAG,CAAC;IACtD,aAAa,EAAE,yBAAyB,CAAC,eAAe,EAAE,IAAI,CAAC;IAC/D,QAAQ,EAAE,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC;IACrD,MAAM,EAAE,yBAAyB,CAAC,QAAQ,EAAE,4BAA4B,CAAC;IACzE,eAAe,EAAE,yBAAyB,CACxC,iBAAiB,EACjB,iDAAiD,CAClD;IACD,UAAU,EAAE,yBAAyB,CACnC,YAAY,EACZ,uCAAuC,CACxC;IACD,WAAW;IACX,SAAS;IACT,aAAa,EAAE,yBAAyB,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5E,eAAe,EAAE,yBAAyB,CACxC,eAAe,EACf,mBAAmB,CACpB;CACF,CAAA;AAED,eAAe,OAAO,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tags/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAWlC,SAAS,yBAAyB,CAChC,IAAY,EACZ,QAAgB;;IAEhB,MAAM,GAAG,SAAG;SAEX;QADQ,WAAQ,GAAG,QAAQ;WAC3B,CAAA;IAED,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAEnD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,OAAO,GAAmC;IAC9C,IAAI;IACJ,YAAY,EAAE,yBAAyB,CAAC,cAAc,EAAE,cAAc,CAAC;IACvE,QAAQ;IACR,WAAW;IACX,gBAAgB,EAAE,yBAAyB,CACzC,kBAAkB,EAClB,mBAAmB,CACpB;IACD,SAAS,EAAE,yBAAyB,CAAC,WAAW,EAAE,GAAG,CAAC;IACtD,aAAa,EAAE,yBAAyB,CAAC,eAAe,EAAE,IAAI,CAAC;IAC/D,WAAW,EAAE,yBAAyB,CAAC,aAAa,EAAE,IAAI,CAAC;IAC3D,QAAQ,EAAE,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC;IACrD,MAAM,EAAE,yBAAyB,CAAC,QAAQ,EAAE,4BAA4B,CAAC;IACzE,eAAe,EAAE,yBAAyB,CACxC,iBAAiB,EACjB,iDAAiD,CAClD;IACD,UAAU,EAAE,yBAAyB,CACnC,YAAY,EACZ,uCAAuC,CACxC;IACD,WAAW;IACX,SAAS;IACT,aAAa,EAAE,yBAAyB,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5E,eAAe,EAAE,yBAAyB,CACxC,eAAe,EACf,mBAAmB,CACpB;IACD,UAAU,EAAE,yBAAyB,CAAC,YAAY,EAAE,YAAY,CAAC;IACjE,KAAK;IACL,SAAS,EAAE,yBAAyB,CAAC,WAAW,EAAE,IAAI,CAAC;IACvD,cAAc,EAAE,yBAAyB,CAAC,gBAAgB,EAAE,IAAI,CAAC;IACjE,QAAQ,EAAE,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC;IACrD,MAAM,EAAE,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACrD,aAAa,EAAE,yBAAyB,CAAC,eAAe,EAAE,GAAG,CAAC;CAC/D,CAAA;AAED,eAAe,OAAO,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/cp-content-pipeline-schema",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -1,11 +1,13 @@
1
1
  import { CapiDataSource } from './capi.js'
2
2
  import { OrigamiImageDataSource } from './origami-image.js'
3
+ import { TwitterDataSource } from './twitter.js'
3
4
  import { URLManagementDataSource } from './url-management.js'
4
5
 
5
6
  export const dataSources = () => ({
6
7
  capi: new CapiDataSource(),
7
8
  origami: new OrigamiImageDataSource(),
8
9
  vanityUrls: new URLManagementDataSource(),
10
+ twitter: new TwitterDataSource(),
9
11
  })
10
12
 
11
13
  export type DataSources = ReturnType<typeof dataSources>
@@ -0,0 +1,9 @@
1
+ import { InstrumentedRESTDataSource } from './instrumented.js'
2
+
3
+ export class TwitterDataSource extends InstrumentedRESTDataSource {
4
+ baseURL = 'https://publish.twitter.com'
5
+
6
+ async getTweet(tweetUrl: string) {
7
+ return this.get(`/oembed?url=${tweetUrl}&omit_script=true`)
8
+ }
9
+ }
package/src/index.ts CHANGED
@@ -21,7 +21,7 @@ import tags from './tags/index.js'
21
21
  import type { Metrics } from 'next-metrics'
22
22
 
23
23
  const tagEntries = Object.entries(tags)
24
- const tagsWithReferences = tagEntries.filter(([_key, tag]) => tag.typeDef)
24
+ const tagsWithReferences = tagEntries.filter(([, tag]) => tag.typeDef)
25
25
  const tagTypeDefs = tagsWithReferences.flatMap(
26
26
  ([_key, tag]) => tag.typeDef as DocumentNode
27
27
  )
@@ -64,4 +64,13 @@ describe('byline transformation', () => {
64
64
  const result = byline.buildBylineTree()
65
65
  expect(result).toMatchSnapshot()
66
66
  })
67
+
68
+ test('handles bylines without any matching authors', async () => {
69
+ const bylineText = 'Chris Giles and Nick Ramsbottom in London'
70
+ const authorUrlMapping = new Map([])
71
+
72
+ const byline = new Byline(bylineText, authorUrlMapping)
73
+ const result = byline.buildBylineTree()
74
+ expect(result).toMatchSnapshot()
75
+ })
67
76
  })
@@ -9,6 +9,7 @@ export class Byline {
9
9
  }
10
10
 
11
11
  #splitBylineByName(byline: string, names: string[]) {
12
+ if (!names.length) return [byline]
12
13
  const regex = new RegExp(`(${names.join('|')})`, 'ig')
13
14
  return byline.split(regex).filter((string) => string !== '')
14
15
  }
@@ -71,6 +71,20 @@ Object {
71
71
  }
72
72
  `;
73
73
 
74
+ exports[`byline transformation handles bylines without any matching authors 1`] = `
75
+ Object {
76
+ "tree": Object {
77
+ "children": Array [
78
+ Object {
79
+ "type": "text",
80
+ "value": "Chris Giles and Nick Ramsbottom in London",
81
+ },
82
+ ],
83
+ "type": "root",
84
+ },
85
+ }
86
+ `;
87
+
74
88
  exports[`byline transformation ignores extra authors in annotations array 1`] = `
75
89
  Object {
76
90
  "tree": Object {
package/src/richText.ts CHANGED
@@ -6,7 +6,7 @@ import { CapiResponse } from './model/CapiResponse.js'
6
6
  import extractReferences from './unified-plugins/extract-references.js'
7
7
  import mapToAbstractTypes from './unified-plugins/map-to-abstract-types.js'
8
8
 
9
- type RichTextResolverData = {
9
+ export type RichTextResolverData = {
10
10
  source: string
11
11
  value: string
12
12
  contentApiData?: CapiResponse
package/src/tags/Link.ts CHANGED
@@ -5,7 +5,7 @@ import type * as hast from 'hast'
5
5
  export const Link: TagConstructor = class Link implements Tag {
6
6
  href: string
7
7
 
8
- static selector = 'a'
8
+ static selector = 'a:not([dataAssetType])'
9
9
 
10
10
  constructor(node: hast.Element) {
11
11
  if (typeof node?.properties?.href === 'string') {
@@ -0,0 +1,82 @@
1
+ import { Tweet } from './Tweet.js'
2
+ import { HastNode, select } from 'hast-util-select'
3
+ import { unified, Plugin } from 'unified'
4
+ import rehypeParse from 'rehype-parse'
5
+ import { QueryContext } from '../index.js'
6
+ import { jest } from '@jest/globals'
7
+
8
+ async function createAST(input: string) {
9
+ const formatOutput: Plugin<[], Element, Element> = function formatOutput() {
10
+ this.Compiler = (tree) => tree
11
+ }
12
+ const body = await unified()
13
+ .use(rehypeParse, { fragment: true })
14
+ .use([formatOutput])
15
+ .process(input)
16
+ return body.result as HastNode
17
+ }
18
+
19
+ describe('Embedded Tweets', () => {
20
+ let body: HastNode
21
+
22
+ const getTweetMock = jest.fn()
23
+ const context = {
24
+ dataSources: {
25
+ twitter: {
26
+ getTweet: getTweetMock,
27
+ },
28
+ },
29
+ } as unknown as QueryContext
30
+
31
+ beforeAll(async () => {
32
+ const input = `<body><a href="https://www.twitter.com/blah" data-asset-type="tweet">Tweet</a></body>`
33
+ body = await createAST(input)
34
+ })
35
+
36
+ afterEach(() => {
37
+ jest.resetAllMocks()
38
+ })
39
+
40
+ it('correctly matches the content API tag', () => {
41
+ const match = select(Tweet.selector, body)
42
+ expect(match).toBeTruthy()
43
+ })
44
+
45
+ it('provides the tweetUrl', () => {
46
+ const node = select(Tweet.selector, body)
47
+ if (!node) {
48
+ throw new Error('Expected a tweet node to be found')
49
+ }
50
+ const tweet = new Tweet(node)
51
+ expect(tweet).toEqual({
52
+ tweetUrl: 'https://www.twitter.com/blah',
53
+ })
54
+ })
55
+
56
+ it('provides the twitter embed code', async () => {
57
+ const node = select(Tweet.selector, body)
58
+ if (!node) {
59
+ throw new Error('Expected a tweet node to be found')
60
+ }
61
+ const tweet = new Tweet(node)
62
+ getTweetMock.mockReturnValue({
63
+ html: '<blockquote>this is an embed</blockquote>',
64
+ })
65
+
66
+ expect(await tweet.embed(context)).toEqual({
67
+ source: 'twitter',
68
+ value: '<blockquote>this is an embed</blockquote>',
69
+ })
70
+ })
71
+
72
+ it('handles errors fetching the tweet embed', async () => {
73
+ const node = select(Tweet.selector, body)
74
+ if (!node) {
75
+ throw new Error('Expected a tweet node to be found')
76
+ }
77
+ const tweet = new Tweet(node)
78
+ getTweetMock.mockRejectedValue(new Error('404'))
79
+
80
+ expect(await tweet.embed(context)).toEqual(null)
81
+ })
82
+ })
@@ -0,0 +1,48 @@
1
+ import { gql } from 'graphql-tag'
2
+ import type { TagConstructor } from './index.js'
3
+ import type * as hast from 'hast'
4
+ import { QueryContext } from '../index.js'
5
+ import type { RichTextResolverData } from '../richText.js'
6
+
7
+ class Tweet {
8
+ tweetUrl: string
9
+
10
+ static selector = 'a[dataAssetType="tweet"]'
11
+
12
+ constructor(node: hast.Element) {
13
+ if (node.properties?.href && typeof node.properties.href === 'string') {
14
+ this.tweetUrl = node.properties.href
15
+ } else {
16
+ throw new Error('Embedded tweet is missing a href')
17
+ }
18
+ }
19
+
20
+ async embed(context: QueryContext): Promise<RichTextResolverData | null> {
21
+ try {
22
+ const tweet = await context.dataSources.twitter.getTweet(this.tweetUrl)
23
+ return {
24
+ source: 'twitter',
25
+ value: tweet.html,
26
+ }
27
+ } catch (err) {
28
+ return null
29
+ }
30
+ }
31
+
32
+ static resolve = {
33
+ embed: async (parent: Tweet, _args: any, context: QueryContext) =>
34
+ parent.embed(context),
35
+ tweetUrl: (parent: Tweet) => parent.tweetUrl,
36
+ }
37
+
38
+ static typeDef = gql`
39
+ type Tweet {
40
+ tweetUrl: String
41
+ embed: RichText
42
+ }
43
+ `
44
+ }
45
+
46
+ const TweetClass: TagConstructor<Tweet> = Tweet
47
+
48
+ export { TweetClass as Tweet }