From fc9ebdf2916b37b3a8fcb3d413cbc533f7923c53 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 22 Jul 2026 17:51:54 -0400 Subject: [PATCH 1/2] eng-2059-refactor-existing-roam-to-cross-app-converter --- .../src/utils/convertRoamNodeToFullContent.ts | 25 +++------------- .../src/utils/roamToCrossAppConverters.ts | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 apps/roam/src/utils/roamToCrossAppConverters.ts diff --git a/apps/roam/src/utils/convertRoamNodeToFullContent.ts b/apps/roam/src/utils/convertRoamNodeToFullContent.ts index 59a056300..c905136a6 100644 --- a/apps/roam/src/utils/convertRoamNodeToFullContent.ts +++ b/apps/roam/src/utils/convertRoamNodeToFullContent.ts @@ -3,9 +3,8 @@ import { type DiscourseNode } from "./getDiscourseNodes"; import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid"; import getPageViewType from "roamjs-components/queries/getPageViewType"; import type { TreeNode, ViewType } from "roamjs-components/types"; -import { contentTypes } from "@repo/content-model"; -import type { CrossAppNode } from "@repo/database/crossAppContracts"; import { crossAppNodeToDbContent } from "@repo/database/lib/crossAppConverters"; +import { fullContentNodeToCrossApp } from "./roamToCrossAppConverters"; import type { LocalContentDataInput } from "@repo/database/inputTypes"; export type RoamFullContentNode = { @@ -16,6 +15,7 @@ export type RoamFullContentNode = { text: string; node_type_id: string; node_title?: string; + fullText?: string; }; const FULL_MARKDOWN_OPTS = { @@ -57,25 +57,8 @@ export const convertRoamNodeToFullContent = ({ const title = node.node_title ?? node.text; const blocks = getFullTreeByParentUid(node.source_local_id).children; const viewType = getPageViewType(title) || "bullet"; - const crossAppNode: CrossAppNode = { - authorId: node.author_local_id, - localId: node.source_local_id, - createdAt: new Date(node.created || Date.now()), - modifiedAt: new Date(node.last_modified || Date.now()), - nodeType: node.node_type_id, - content: { - direct: { - localId: node.source_local_id, - value: title, - }, - full: { - localId: node.source_local_id, - value: buildFullMarkdown({ title, blocks, viewType }), - contentType: contentTypes.roamMarkdown, - scale: "document", - }, - }, - }; + node.fullText = buildFullMarkdown({ title, blocks, viewType }); + const crossAppNode = fullContentNodeToCrossApp(node); const fullContent = crossAppNodeToDbContent(crossAppNode, "full"); return fullContent === undefined ? [] : [fullContent]; } catch (error) { diff --git a/apps/roam/src/utils/roamToCrossAppConverters.ts b/apps/roam/src/utils/roamToCrossAppConverters.ts new file mode 100644 index 000000000..e96f8b46e --- /dev/null +++ b/apps/roam/src/utils/roamToCrossAppConverters.ts @@ -0,0 +1,29 @@ +import type { CrossAppNode } from "@repo/database/crossAppContracts"; +import type { RoamFullContentNode } from "./convertRoamNodeToFullContent"; +import { contentTypes } from "@repo/content-model"; + +export const fullContentNodeToCrossApp = ( + node: RoamFullContentNode, +): CrossAppNode => { + return { + authorId: node.author_local_id, + localId: node.source_local_id, + createdAt: new Date(node.created || Date.now()), + modifiedAt: new Date(node.last_modified || Date.now()), + nodeType: node.node_type_id, + content: { + direct: { + localId: node.source_local_id, + value: node.node_title ?? node.text, + }, + full: node.fullText + ? { + localId: node.source_local_id, + value: node.fullText, + contentType: contentTypes.roamMarkdown, + scale: "document", + } + : undefined, + }, + }; +}; From 1be324d6c3fce48269de967bcfaffbc3d670de6a Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Thu, 23 Jul 2026 16:45:44 -0400 Subject: [PATCH 2/2] Make it closer to a simple move --- .../convertRoamNodeToFullContent.example.ts | 2 +- ...ertRoamNodeToFullContent.simple.example.ts | 2 +- .../src/utils/convertRoamNodeToFullContent.ts | 39 -------------- .../src/utils/roamToCrossAppConverters.ts | 53 ++++++++++++++++--- 4 files changed, 47 insertions(+), 49 deletions(-) diff --git a/apps/roam/src/utils/convertRoamNodeToFullContent.example.ts b/apps/roam/src/utils/convertRoamNodeToFullContent.example.ts index f425d7d9f..85547c1a7 100644 --- a/apps/roam/src/utils/convertRoamNodeToFullContent.example.ts +++ b/apps/roam/src/utils/convertRoamNodeToFullContent.example.ts @@ -1,7 +1,7 @@ import { contentTypes } from "@repo/content-model"; import type { TreeNode } from "roamjs-components/types"; import type { CrossAppNode } from "@repo/database/crossAppContracts"; -import { buildFullMarkdown } from "./convertRoamNodeToFullContent"; +import { buildFullMarkdown } from "./roamToCrossAppConverters"; /** * Example Roam page tree used to show the markdown emitted for a `full` content diff --git a/apps/roam/src/utils/convertRoamNodeToFullContent.simple.example.ts b/apps/roam/src/utils/convertRoamNodeToFullContent.simple.example.ts index cef2e3160..c17457662 100644 --- a/apps/roam/src/utils/convertRoamNodeToFullContent.simple.example.ts +++ b/apps/roam/src/utils/convertRoamNodeToFullContent.simple.example.ts @@ -1,6 +1,6 @@ import type { TreeNode } from "roamjs-components/types"; import type { CrossAppNode } from "@repo/database/crossAppContracts"; -import { buildFullMarkdown } from "./convertRoamNodeToFullContent"; +import { buildFullMarkdown } from "./roamToCrossAppConverters"; import { contentTypes } from "@repo/content-model"; const block = (text: string, children: TreeNode[] = []): TreeNode => ({ diff --git a/apps/roam/src/utils/convertRoamNodeToFullContent.ts b/apps/roam/src/utils/convertRoamNodeToFullContent.ts index c905136a6..d1f24cba1 100644 --- a/apps/roam/src/utils/convertRoamNodeToFullContent.ts +++ b/apps/roam/src/utils/convertRoamNodeToFullContent.ts @@ -1,8 +1,3 @@ -import { toMarkdown } from "./pageToMarkdown"; -import { type DiscourseNode } from "./getDiscourseNodes"; -import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid"; -import getPageViewType from "roamjs-components/queries/getPageViewType"; -import type { TreeNode, ViewType } from "roamjs-components/types"; import { crossAppNodeToDbContent } from "@repo/database/lib/crossAppConverters"; import { fullContentNodeToCrossApp } from "./roamToCrossAppConverters"; import type { LocalContentDataInput } from "@repo/database/inputTypes"; @@ -15,36 +10,6 @@ export type RoamFullContentNode = { text: string; node_type_id: string; node_title?: string; - fullText?: string; -}; - -const FULL_MARKDOWN_OPTS = { - refs: true, - embeds: true, - simplifiedFilename: false, - removeSpecialCharacters: false, - maxFilenameLength: 64, - linkType: "alias", - allNodes: [] as DiscourseNode[], -}; - -export const buildFullMarkdown = ({ - title, - blocks, - viewType = "bullet", -}: { - title: string; - blocks: TreeNode[]; - viewType?: ViewType; -}): string => { - const body = blocks - .filter((block) => !!block.text || !!block.children?.length) - .map((block) => - toMarkdown({ c: block, v: viewType, i: 0, opts: FULL_MARKDOWN_OPTS }), - ) - .join("\n") - .trim(); - return body ? `# ${title}\n\n${body}\n` : `# ${title}\n`; }; export const convertRoamNodeToFullContent = ({ @@ -54,10 +19,6 @@ export const convertRoamNodeToFullContent = ({ }): LocalContentDataInput[] => nodes.flatMap((node) => { try { - const title = node.node_title ?? node.text; - const blocks = getFullTreeByParentUid(node.source_local_id).children; - const viewType = getPageViewType(title) || "bullet"; - node.fullText = buildFullMarkdown({ title, blocks, viewType }); const crossAppNode = fullContentNodeToCrossApp(node); const fullContent = crossAppNodeToDbContent(crossAppNode, "full"); return fullContent === undefined ? [] : [fullContent]; diff --git a/apps/roam/src/utils/roamToCrossAppConverters.ts b/apps/roam/src/utils/roamToCrossAppConverters.ts index e96f8b46e..011069c66 100644 --- a/apps/roam/src/utils/roamToCrossAppConverters.ts +++ b/apps/roam/src/utils/roamToCrossAppConverters.ts @@ -1,10 +1,49 @@ import type { CrossAppNode } from "@repo/database/crossAppContracts"; import type { RoamFullContentNode } from "./convertRoamNodeToFullContent"; +import type { DiscourseNode } from "./getDiscourseNodes"; +import type { TreeNode, ViewType } from "roamjs-components/types"; +import { toMarkdown } from "./pageToMarkdown"; +import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid"; +import getPageViewType from "roamjs-components/queries/getPageViewType"; import { contentTypes } from "@repo/content-model"; +const FULL_MARKDOWN_OPTS = { + refs: true, + embeds: true, + simplifiedFilename: false, + removeSpecialCharacters: false, + maxFilenameLength: 64, + linkType: "alias", + allNodes: [] as DiscourseNode[], +}; + +export const buildFullMarkdown = ({ + title, + blocks, + viewType = "bullet", +}: { + title: string; + blocks: TreeNode[]; + viewType?: ViewType; +}): string => { + const body = blocks + .filter((block) => !!block.text || !!block.children?.length) + .map((block) => + toMarkdown({ c: block, v: viewType, i: 0, opts: FULL_MARKDOWN_OPTS }), + ) + .join("\n") + .trim(); + return body ? `# ${title}\n\n${body}\n` : `# ${title}\n`; +}; + export const fullContentNodeToCrossApp = ( node: RoamFullContentNode, ): CrossAppNode => { + const title = node.node_title ?? node.text; + const blocks = getFullTreeByParentUid(node.source_local_id).children; + const viewType = getPageViewType(title) || "bullet"; + const fullText = buildFullMarkdown({ title, blocks, viewType }); + return { authorId: node.author_local_id, localId: node.source_local_id, @@ -16,14 +55,12 @@ export const fullContentNodeToCrossApp = ( localId: node.source_local_id, value: node.node_title ?? node.text, }, - full: node.fullText - ? { - localId: node.source_local_id, - value: node.fullText, - contentType: contentTypes.roamMarkdown, - scale: "document", - } - : undefined, + full: { + localId: node.source_local_id, + value: fullText, + contentType: contentTypes.roamMarkdown, + scale: "document", + }, }, }; };