From da0d4da4d5a39c865dd68d78a14ee3533adcb943 Mon Sep 17 00:00:00 2001 From: Maxime Thirouin Date: Wed, 2 May 2018 21:55:57 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20Add=20a=20new=20helper=20to=20ex?= =?UTF-8?q?tract=20meta=20from=20body=20nodes=20(title=20+=20headers=20lis?= =?UTF-8?q?t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extractMetaFromBodyNode.js.snap | 25 ++++ .../src/__tests__/extractMetaFromBodyNode.js | 117 ++++++++++++++++++ .../src/extractMetaFromBodyNode.js | 43 +++++++ 3 files changed, 185 insertions(+) create mode 100644 packages/helpers-transform/src/__tests__/__snapshots__/extractMetaFromBodyNode.js.snap create mode 100644 packages/helpers-transform/src/__tests__/extractMetaFromBodyNode.js create mode 100644 packages/helpers-transform/src/extractMetaFromBodyNode.js diff --git a/packages/helpers-transform/src/__tests__/__snapshots__/extractMetaFromBodyNode.js.snap b/packages/helpers-transform/src/__tests__/__snapshots__/extractMetaFromBodyNode.js.snap new file mode 100644 index 000000000..010db2eba --- /dev/null +++ b/packages/helpers-transform/src/__tests__/__snapshots__/extractMetaFromBodyNode.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should guess partial data from body AST 1`] = ` +Object { + "headers": Array [ + Array [ + 1, + "Auto title", + ], + Array [ + 2, + "Sub title", + ], + Array [ + 2, + "Another Level 2", + ], + Array [ + 3, + "A level 3", + ], + ], + "title": "Auto title", +} +`; diff --git a/packages/helpers-transform/src/__tests__/extractMetaFromBodyNode.js b/packages/helpers-transform/src/__tests__/extractMetaFromBodyNode.js new file mode 100644 index 000000000..b78e65491 --- /dev/null +++ b/packages/helpers-transform/src/__tests__/extractMetaFromBodyNode.js @@ -0,0 +1,117 @@ +// @flow + +import extractMetaFromBodyNode from "../extractMetaFromBodyNode.js"; + +it("should guess partial data from body AST", () => { + expect( + extractMetaFromBodyNode({ + c: [ + { + c: [ + { + c: [ + { + p: { + className: "icon icon-link" + }, + t: "span" + } + ], + p: { + "aria-hidden": true, + href: "#auto-title" + }, + t: "a" + }, + "Auto title" + ], + p: { + id: "auto-title" + }, + t: "h1" + }, + "\n", + { + c: [ + { + c: [ + { + p: { + className: "icon icon-link" + }, + t: "span" + } + ], + p: { + "aria-hidden": true, + href: "#sub-title" + }, + t: "a" + }, + "Sub title" + ], + p: { + id: "sub-title" + }, + t: "h2" + }, + "\n", + { + c: ["Content"], + t: "p" + }, + "\n", + { + c: [ + { + c: [ + { + p: { + className: "icon icon-link" + }, + t: "span" + } + ], + p: { + "aria-hidden": true, + href: "#another-level-2" + }, + t: "a" + }, + "Another Level 2" + ], + p: { + id: "another-level-2" + }, + t: "h2" + }, + "\n", + { + c: [ + { + c: [ + { + p: { + className: "icon icon-link" + }, + t: "span" + } + ], + p: { + "aria-hidden": true, + href: "#a-level-3" + }, + t: "a" + }, + "A level 3" + ], + p: { + id: "a-level-3" + }, + t: "h3" + } + ], + t: "div" + }) + ).toMatchSnapshot(); +}); diff --git a/packages/helpers-transform/src/extractMetaFromBodyNode.js b/packages/helpers-transform/src/extractMetaFromBodyNode.js new file mode 100644 index 000000000..49417e2c5 --- /dev/null +++ b/packages/helpers-transform/src/extractMetaFromBodyNode.js @@ -0,0 +1,43 @@ +// @flow + +type Node = + | string + | {| + t?: string, + p?: Object, + c?: Node | $ReadOnlyArray + |}; + +const renderText = (node?: Node) => { + if (!node) return ""; + if (typeof node === "string") return node; + return Array.isArray(node.c) + ? node.c.map((child: Node) => renderText(child)).join("") + : renderText(node.c); +}; + +const getHeaders = (node?: Node) => { + if (!node) return []; + if (typeof node.t === "string") { + const tag = node.t; + const level = parseInt(tag[1], 10); + if (tag[0] === "h" && !isNaN(level)) { + return [[level, renderText(node)]]; + } + } + return (Array.isArray(node.c) + ? // $FlowFixMe stfu + node.c.reduce((acc, child: Node) => acc.concat(getHeaders(child)), []) + : // $FlowFixMe stfu + getHeaders(node.c) + ).filter(h => h); +}; + +export default (node: Node) => { + const headers = getHeaders(node); + const firstH1 = headers.find(h => h[0] === 1); + return { + ...(firstH1 ? { title: firstH1[1] } : {}), + ...(headers.length > 0 ? { headers } : {}) + }; +};