diff --git a/packages/lib/src/AdfPostProcess.ts b/packages/lib/src/AdfPostProcess.ts deleted file mode 100644 index 31086946..00000000 --- a/packages/lib/src/AdfPostProcess.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { traverse } from "@atlaskit/adf-utils/traverse"; -import { JSONDocNode } from "@atlaskit/editor-json-transformer"; -import { ConfluenceAdfFile, ConfluenceNode } from "./Publisher"; -import { ConfluenceSettings } from "./Settings"; -import { marksEqual } from "./AdfEqual"; -import { ADFEntity } from "@atlaskit/adf-utils/types"; -import { p } from "@atlaskit/adf-utils/builders"; - -export function prepareAdf( - confluencePagesToPublish: ConfluenceNode[], - settings: ConfluenceSettings -) { - const fileToPageIdMap: Record = {}; - - confluencePagesToPublish.forEach((node) => { - fileToPageIdMap[node.file.fileName] = node.file; - }); - - confluencePagesToPublish.forEach((confluenceNode) => { - let result = confluenceNode.file.contents; - - if (result.content.length === 0) { - result.content = [p()]; - } - - result = traverse(result, { - text: (node, _parent) => { - if ( - node.marks && - node.marks[0].type === "link" && - node.marks[0].attrs - ) { - if ( - typeof node.marks[0].attrs.href === "string" && - node.marks[0].attrs.href.startsWith("wikilink") - ) { - const wikilinkUrl = new URL(node.marks[0].attrs.href); - const pagename = `${wikilinkUrl.pathname}.md`; - - const linkPage = fileToPageIdMap[pagename]; - if (linkPage) { - const confluenceUrl = `${settings.confluenceBaseUrl}/wiki/spaces/${linkPage.spaceKey}/pages/${linkPage.pageId}${wikilinkUrl.hash}`; - node.marks[0].attrs.href = confluenceUrl; - if (node.text === wikilinkUrl.pathname) { - node.type = "inlineCard"; - node.attrs = { - url: node.marks[0].attrs.href, - }; - delete node.marks; - delete node.text; - return node; - } - } else { - delete node.marks[0]; - } - return node; - } - } - }, - }) as JSONDocNode; - - result = traverse(result, { - any: (node, _parent) => { - if ( - node.content && - node.content.filter((m) => !(m === undefined || m === null)) - .length === 0 - ) { - delete node.content; - } - - if ( - node.marks && - node.marks.filter((m) => !(m === undefined || m === null)) - .length === 0 - ) { - delete node.marks; - } - return node; - }, - }) as JSONDocNode; - - result = traverse(result, { - paragraph: (node, _parent) => { - if ( - node?.content === undefined || - node.content.filter((m) => !(m === undefined || m === null)) - .length === 0 - ) { - return node; - } - const processedContent: Array = []; - const indexToSkip: number[] = []; - const nodesToCheck = node.content.length ?? 0; - for (let index = 0; index < nodesToCheck - 1; index++) { - if (indexToSkip.includes(index)) { - continue; - } - - const currentNode = node.content[index]; - const nextNode = node.content[index + 1]; - - if ( - nextNode === undefined || - currentNode === undefined || - currentNode.type !== "text" || - nextNode.type !== "text" - ) { - processedContent.push(currentNode); - continue; - } - - for ( - let lookAheadIndex = index + 1; - lookAheadIndex < nodesToCheck - 1; - lookAheadIndex++ - ) { - const futureNode = node.content[lookAheadIndex]; - - if (marksEqual(currentNode?.marks, futureNode?.marks)) { - currentNode.text = - currentNode.text ?? - "" + (futureNode?.text ?? ""); - indexToSkip.push(lookAheadIndex); - } else { - break; - } - } - - processedContent.push(currentNode); - } - - if (processedContent.length > 0) { - node.content = processedContent; - } - - return node; - }, - }) as JSONDocNode; - - confluenceNode.file.contents = result; - }); -} diff --git a/packages/lib/src/AdfProcessing.ts b/packages/lib/src/AdfProcessing.ts new file mode 100644 index 00000000..b6edd442 --- /dev/null +++ b/packages/lib/src/AdfProcessing.ts @@ -0,0 +1,187 @@ +import { traverse } from "@atlaskit/adf-utils/traverse"; +import { JSONDocNode } from "@atlaskit/editor-json-transformer"; +import { ConfluenceAdfFile, ConfluenceNode } from "./Publisher"; +import { ConfluenceSettings } from "./Settings"; +import { marksEqual } from "./AdfEqual"; +import { ADFEntity, ADFEntityMark } from "@atlaskit/adf-utils/types"; +import { p, tr } from "@atlaskit/adf-utils/builders"; + +export function prepareAdfToUpload( + confluencePagesToPublish: ConfluenceNode[], + settings: ConfluenceSettings +) { + const fileToPageIdMap: Record = {}; + + confluencePagesToPublish.forEach((node) => { + fileToPageIdMap[node.file.fileName] = node.file; + }); + + confluencePagesToPublish.forEach((confluenceNode) => { + let result = confluenceNode.file.contents; + + if (result.content.length === 0) { + result.content = [p()]; + } + + result = processWikilinkToActualLink(result, fileToPageIdMap, settings); + + result = mergeTextNodes(result); + + confluenceNode.file.contents = result; + }); +} + +function processWikilinkToActualLink( + adf: JSONDocNode, + fileToPageIdMap: Record, + settings: ConfluenceSettings +) { + return traverse(adf, { + text: (node, _parent) => { + if ( + node.marks && + node.marks[0].type === "link" && + node.marks[0].attrs + ) { + if ( + typeof node.marks[0].attrs.href === "string" && + node.marks[0].attrs.href.startsWith("wikilink") + ) { + const wikilinkUrl = new URL(node.marks[0].attrs.href); + const pagename = `${wikilinkUrl.pathname}.md`; + + const linkPage = fileToPageIdMap[pagename]; + if (linkPage) { + const confluenceUrl = `${settings.confluenceBaseUrl}/wiki/spaces/${linkPage.spaceKey}/pages/${linkPage.pageId}${wikilinkUrl.hash}`; + node.marks[0].attrs.href = confluenceUrl; + if (node.text === wikilinkUrl.pathname) { + node.type = "inlineCard"; + node.attrs = { + url: node.marks[0].attrs.href, + }; + delete node.marks; + delete node.text; + return node; + } + } else { + delete node.marks[0]; + } + return node; + } + } + }, + }) as JSONDocNode; +} + +function removeEmptyProperties(adf: JSONDocNode) { + return traverse(adf, { + any: (node, _parent) => { + if ( + node.content && + node.content.filter((m) => !(m === undefined || m === null)) + .length === 0 + ) { + delete node.content; + } + + try { + if ( + node.marks && + node.marks.filter((m) => !(m === undefined || m === null)) + .length === 0 + ) { + delete node.marks; + } + } catch (e: unknown) { + console.log({ marks: node.marks, e }); + } + return node; + }, + }) as JSONDocNode; +} + +function mergeTextNodes(adf: JSONDocNode) { + let result = removeEmptyProperties(adf); + + result = traverse(result, { + paragraph: (node, _parent) => { + if ( + node?.content === undefined || + node.content.filter((m) => !(m === undefined || m === null)) + .length === 0 + ) { + return node; + } + const processedContent: Array = []; + const indexToSkip: number[] = []; + const nodesToCheck = node.content.length ?? 0; + for (let index = 0; index < nodesToCheck; index++) { + if (indexToSkip.includes(index)) { + continue; + } + + const currentNode = node.content[index]; + const nextNode = node.content[index + 1]; + + if ( + nextNode === undefined || + currentNode === undefined || + currentNode.type !== "text" || + nextNode.type !== "text" + ) { + processedContent.push(currentNode); + continue; + } + + for ( + let lookAheadIndex = index + 1; + lookAheadIndex < nodesToCheck; + lookAheadIndex++ + ) { + const futureNode = node.content[lookAheadIndex]; + + if (marksEqual(currentNode?.marks, futureNode?.marks)) { + currentNode.text = + (currentNode.text ?? "") + (futureNode?.text ?? ""); + indexToSkip.push(lookAheadIndex); + } else { + break; + } + } + + processedContent.push(currentNode); + } + + if (processedContent.length > 0) { + node.content = processedContent; + } + + return node; + }, + }) as JSONDocNode; + + return result; +} + +export function removeInlineComments(adf: JSONDocNode) { + let result = traverse(adf, { + text: (node, _parent) => { + if (node.marks) { + node.marks = node.marks.reduce((prev, curr) => { + if ( + curr.type === "annotation" && + curr.attrs?.annotationType === "inlineComment" + ) { + return prev; + } + return [...prev, curr]; + }, [] as ADFEntityMark[]); + } + return node; + }, + }) as JSONDocNode; + + result = mergeTextNodes(result); + + return result; +} diff --git a/packages/lib/src/MdToADF.ts b/packages/lib/src/MdToADF.ts index cbfe10e9..70ac7df6 100644 --- a/packages/lib/src/MdToADF.ts +++ b/packages/lib/src/MdToADF.ts @@ -36,13 +36,19 @@ export class MdToADF { node.marks && node.marks[0].type === "link" && node.marks[0].attrs && - node.marks[0].attrs.href + "href" in node.marks[0].attrs ) ) { return; } - if (!isSafeUrl(node.marks[0].attrs.href)) { + if ( + node.marks[0].attrs.href === "" || + (!isSafeUrl(node.marks[0].attrs.href) && + !(node.marks[0].attrs.href as string).startsWith( + "wikilink" + )) + ) { node.marks[0].attrs.href = "#"; } diff --git a/packages/lib/src/Publisher.ts b/packages/lib/src/Publisher.ts index 3f318665..d69f1e4c 100644 --- a/packages/lib/src/Publisher.ts +++ b/packages/lib/src/Publisher.ts @@ -5,7 +5,7 @@ import { JSONDocNode } from "@atlaskit/editor-json-transformer"; import { createFolderStructure as createLocalAdfTree } from "./TreeLocal"; import { ensureAllFilesExistInConfluence } from "./TreeConfluence"; import { uploadBuffer, UploadedImageData, uploadFile } from "./Attachments"; -import { prepareAdf } from "./AdfPostProcess"; +import { prepareAdfToUpload, removeInlineComments } from "./AdfProcessing"; import { adfEqual } from "./AdfEqual"; import { getMermaidFileName, @@ -144,7 +144,7 @@ export class Publisher { parentPage.id ); - prepareAdf(confluencePagesToPublish, this.settings); + prepareAdfToUpload(confluencePagesToPublish, this.settings); if (publishFilter) { confluencePagesToPublish = confluencePagesToPublish.filter( @@ -265,14 +265,17 @@ export class Publisher { imageResult["uploaded"] > 0 ? "updated" : "same"; } - if (!adfEqual(JSON.parse(currentContents), uploadResult.adf)) { + const cleanedExistingContents = removeInlineComments( + JSON.parse(currentContents) + ); + if (!adfEqual(cleanedExistingContents, uploadResult.adf)) { result.contentResult = "updated"; console.log(`TESTING DIFF - ${adfFile.absoluteFilePath}`); const replacer = (key: unknown, value: unknown) => typeof value === "undefined" ? null : value; - console.log(JSON.stringify(JSON.parse(currentContents), replacer)); + console.log(JSON.stringify(cleanedExistingContents, replacer)); console.log(JSON.stringify(uploadResult.adf, replacer)); const updateContentDetails = { @@ -441,8 +444,6 @@ export class Publisher { }; } - console.log({ imageMap }); - let afterAdf = adr as ADFEntity; afterAdf =