fix(replacer): errors suppressed by catch statement (#2856)

This commit is contained in:
SebKranz
2024-11-28 11:35:11 +01:00
committed by GitHub
parent 3654eb0800
commit 3997ce538d
3 changed files with 63 additions and 56 deletions

View File

@ -132,38 +132,37 @@ export const patchDocument = async <T extends PatchDocumentOutputType = PatchDoc
// We need to loop through to catch every occurrence of the patch text // We need to loop through to catch every occurrence of the patch text
// It is possible that the patch text is in the same run // It is possible that the patch text is in the same run
// This algorithm is limited to one patch per text run // This algorithm is limited to one patch per text run
// Once it cannot find any more occurrences, it will throw an error, and then we break out of the loop // We break out of the loop once it cannot find any more occurrences
// https://github.com/dolanmiu/docx/issues/2267 // https://github.com/dolanmiu/docx/issues/2267
while (true) { while (true) {
try { const { didFindOccurrence } = replacer({
replacer({ json,
json, patch: {
patch: { ...patchValue,
...patchValue, children: patchValue.children.map((element) => {
children: patchValue.children.map((element) => { // We need to replace external hyperlinks with concrete hyperlinks
// We need to replace external hyperlinks with concrete hyperlinks if (element instanceof ExternalHyperlink) {
if (element instanceof ExternalHyperlink) { const concreteHyperlink = new ConcreteHyperlink(element.options.children, uniqueId());
const concreteHyperlink = new ConcreteHyperlink(element.options.children, uniqueId()); // eslint-disable-next-line functional/immutable-data
// eslint-disable-next-line functional/immutable-data hyperlinkRelationshipAdditions.push({
hyperlinkRelationshipAdditions.push({ key,
key, hyperlink: {
hyperlink: { id: concreteHyperlink.linkId,
id: concreteHyperlink.linkId, link: element.options.link,
link: element.options.link, },
}, });
}); return concreteHyperlink;
return concreteHyperlink; } else {
} else { return element;
return element; }
} }),
}), // eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any } as any,
} as any, patchText,
patchText, context,
context, keepOriginalStyles,
keepOriginalStyles, });
}); if (!didFindOccurrence) {
} catch {
break; break;
} }
} }

View File

@ -78,24 +78,23 @@ export const MOCK_JSON = {
describe("replacer", () => { describe("replacer", () => {
describe("replacer", () => { describe("replacer", () => {
it("should throw an error if nothing is added", () => { it("should throw an error if nothing is added", () => {
expect(() => const { didFindOccurrence } = replacer({
replacer({ json: {
json: { elements: [],
elements: [], },
}, patch: {
patch: { type: PatchType.PARAGRAPH,
type: PatchType.PARAGRAPH, children: [],
children: [], },
}, patchText: "hello",
patchText: "hello", // eslint-disable-next-line functional/prefer-readonly-type
// eslint-disable-next-line functional/prefer-readonly-type context: vi.fn<[], IContext>()(),
context: vi.fn<[], IContext>()(), });
}), expect(didFindOccurrence).toBe(false);
).toThrow();
}); });
it("should replace paragraph type", () => { it("should replace paragraph type", () => {
const output = replacer({ const { element, didFindOccurrence } = replacer({
json: JSON.parse(JSON.stringify(MOCK_JSON)), json: JSON.parse(JSON.stringify(MOCK_JSON)),
patch: { patch: {
type: PatchType.PARAGRAPH, type: PatchType.PARAGRAPH,
@ -111,11 +110,12 @@ describe("replacer", () => {
}, },
}); });
expect(JSON.stringify(output)).to.contain("Delightful Header"); expect(JSON.stringify(element)).to.contain("Delightful Header");
expect(didFindOccurrence).toBe(true);
}); });
it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => { it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
const output = replacer({ const { element, didFindOccurrence } = replacer({
json: JSON.parse(JSON.stringify(MOCK_JSON)), json: JSON.parse(JSON.stringify(MOCK_JSON)),
patch: { patch: {
type: PatchType.PARAGRAPH, type: PatchType.PARAGRAPH,
@ -132,8 +132,8 @@ describe("replacer", () => {
keepOriginalStyles: true, keepOriginalStyles: true,
}); });
expect(JSON.stringify(output)).to.contain("sweet"); expect(JSON.stringify(element)).to.contain("sweet");
expect(output.elements![0].elements![1].elements).toMatchObject([ expect(element.elements![0].elements![1].elements).toMatchObject([
{ {
type: "element", type: "element",
name: "w:r", name: "w:r",
@ -187,10 +187,11 @@ describe("replacer", () => {
], ],
}, },
]); ]);
expect(didFindOccurrence).toBe(true);
}); });
it("should replace document type", () => { it("should replace document type", () => {
const output = replacer({ const { element, didFindOccurrence } = replacer({
json: JSON.parse(JSON.stringify(MOCK_JSON)), json: JSON.parse(JSON.stringify(MOCK_JSON)),
patch: { patch: {
type: PatchType.DOCUMENT, type: PatchType.DOCUMENT,
@ -206,12 +207,13 @@ describe("replacer", () => {
}, },
}); });
expect(JSON.stringify(output)).to.contain("Lorem ipsum paragraph"); expect(JSON.stringify(element)).to.contain("Lorem ipsum paragraph");
expect(didFindOccurrence).toBe(true);
}); });
it("should replace", () => { it("should replace", () => {
// cspell:disable // cspell:disable
const output = replacer({ const { element, didFindOccurrence } = replacer({
json: { json: {
elements: [ elements: [
{ {
@ -655,7 +657,8 @@ describe("replacer", () => {
}, },
}); });
expect(JSON.stringify(output)).to.contain("Lorem ipsum paragraph"); expect(JSON.stringify(element)).to.contain("Lorem ipsum paragraph");
expect(didFindOccurrence).toBe(true);
}); });
}); });
}); });

View File

@ -14,6 +14,11 @@ const formatter = new Formatter();
const SPLIT_TOKEN = "ɵ"; const SPLIT_TOKEN = "ɵ";
type IReplacerResult = {
readonly element: Element;
readonly didFindOccurrence: boolean;
};
export const replacer = ({ export const replacer = ({
json, json,
patch, patch,
@ -26,11 +31,11 @@ export const replacer = ({
readonly patchText: string; readonly patchText: string;
readonly context: IContext; readonly context: IContext;
readonly keepOriginalStyles?: boolean; readonly keepOriginalStyles?: boolean;
}): Element => { }): IReplacerResult => {
const renderedParagraphs = findLocationOfText(json, patchText); const renderedParagraphs = findLocationOfText(json, patchText);
if (renderedParagraphs.length === 0) { if (renderedParagraphs.length === 0) {
throw new Error(`Could not find text ${patchText}`); return { element: json, didFindOccurrence: false };
} }
for (const renderedParagraph of renderedParagraphs) { for (const renderedParagraph of renderedParagraphs) {
@ -85,7 +90,7 @@ export const replacer = ({
} }
} }
return json; return { element: json, didFindOccurrence: true };
}; };
const goToElementFromPath = (json: Element, path: readonly number[]): Element => { const goToElementFromPath = (json: Element, path: readonly number[]): Element => {