Skip to content

Commit

Permalink
fix(client): don't break LaTeX when replacing carriage returns
Browse files Browse the repository at this point in the history
Fixes #917
  • Loading branch information
remarkablemark committed Dec 16, 2024
1 parent 013e027 commit d69bc66
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 27 deletions.
8 changes: 8 additions & 0 deletions src/client/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,11 @@ export const CASE_SENSITIVE_TAG_NAMES_MAP = CASE_SENSITIVE_TAG_NAMES.reduce(
},
{} as Record<string, string>,
);

export const CARRIAGE_RETURN = '\r';
export const CARRIAGE_RETURN_REGEX = new RegExp(CARRIAGE_RETURN, 'g');
export const CARRIAGE_RETURN_PLACEHOLDER = `__HTML_DOM_PARSER_CARRIAGE_RETURN_PLACEHOLDER_${Date.now()}__`;
export const CARRIAGE_RETURN_PLACEHOLDER_REGEX = new RegExp(
CARRIAGE_RETURN_PLACEHOLDER,
'g',
);
12 changes: 9 additions & 3 deletions src/client/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Comment, Element, ProcessingInstruction, Text } from 'domhandler';

import type { DOMNode } from '../types';
import { CASE_SENSITIVE_TAG_NAMES_MAP } from './constants';
import {
CARRIAGE_RETURN,
CARRIAGE_RETURN_PLACEHOLDER,
CARRIAGE_RETURN_PLACEHOLDER_REGEX,
CARRIAGE_RETURN_REGEX,
CASE_SENSITIVE_TAG_NAMES_MAP,
} from './constants';

/**
* Gets case-sensitive tag name.
Expand Down Expand Up @@ -58,7 +64,7 @@ function formatTagName(tagName: string): string {
* @returns - HTML string with escaped special characters.
*/
export function escapeSpecialCharacters(html: string): string {
return html.replace(/\r/g, '\\r');
return html.replace(CARRIAGE_RETURN_REGEX, CARRIAGE_RETURN_PLACEHOLDER);
}

/**
Expand All @@ -68,7 +74,7 @@ export function escapeSpecialCharacters(html: string): string {
* @returns - Text with escaped characters reverted.
*/
export function revertEscapedCharacters(text: string): string {
return text.replace(/\\r/g, '\r');
return text.replace(CARRIAGE_RETURN_PLACEHOLDER_REGEX, CARRIAGE_RETURN);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions test/cases/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ module.exports = [
data: '<div>Hello\r<span>Beautiful\r</span>World</div>',
},

// LaTeX
{
name: 'LaTeX',
data: '<span class="math">\\left(\\right)\\rD\\rightarrow\\reals\\ni</span>',
},
{
name: 'LaTeX with carriage return',
data: '<span class="math">\\left(\\right)\\rD\\rightarrow\\reals\\ni</span>\r\n',
},

// custom tag
{
name: 'custom tag',
Expand Down
2 changes: 2 additions & 0 deletions test/cases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ module.exports = {
html,
svg,
};

module.exports.default = module.exports;
57 changes: 33 additions & 24 deletions test/server/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect } from 'chai';

import { CARRIAGE_RETURN_PLACEHOLDER } from '../../src/client/constants';
import { formatDOM } from '../../src/client/utilities';
import { revertEscapedCharacters } from '../../src/client/utilities';
import { escapeSpecialCharacters } from '../../src/client/utilities';
Expand All @@ -15,59 +16,67 @@ describe('client utilities', () => {

describe('escapeSpecialCharacters', () => {
it('escapes carriage return characters', () => {
const input = 'Hello\rWorld';
const expected = 'Hello\\rWorld';
expect(escapeSpecialCharacters(input)).to.equal(expected);
expect(escapeSpecialCharacters('Hello\rWorld')).to.equal(
`Hello${CARRIAGE_RETURN_PLACEHOLDER}World`,
);
});

it('does not modify strings without special characters', () => {
const input = 'Hello World';
expect(escapeSpecialCharacters(input)).to.equal(input);
const text = 'Hello World';
expect(escapeSpecialCharacters(text)).to.equal(text);
});

it('handles empty strings', () => {
expect(escapeSpecialCharacters('')).to.equal('');
const text = '';
expect(escapeSpecialCharacters(text)).to.equal(text);
});

it('handles multiple carriage returns', () => {
const input = 'Hello\rDear\rWorld';
const expected = 'Hello\\rDear\\rWorld';
expect(escapeSpecialCharacters(input)).to.equal(expected);
expect(escapeSpecialCharacters('Hello\rDear\rWorld')).to.equal(
`Hello${CARRIAGE_RETURN_PLACEHOLDER}Dear${CARRIAGE_RETURN_PLACEHOLDER}World`,
);
});

it('only escapes carriage returns', () => {
const input = 'Hello\rWorld\n'; // \n should not be affected
const expected = 'Hello\\rWorld\n';
expect(escapeSpecialCharacters(input)).to.equal(expected);
// `\n` and `\right` should not be affected
expect(escapeSpecialCharacters('Hello\rWorld\n\right')).to.equal(
`Hello${CARRIAGE_RETURN_PLACEHOLDER}World\n${CARRIAGE_RETURN_PLACEHOLDER}ight`,
);
});
});

describe('revertEscapedCharacters', () => {
it('reverts escaped carriage return characters', () => {
const input = 'Hello\\rWorld';
const expected = 'Hello\rWorld';
expect(revertEscapedCharacters(input)).to.equal(expected);
expect(
revertEscapedCharacters(`Hello${CARRIAGE_RETURN_PLACEHOLDER}World`),
).to.equal('Hello\rWorld');
});

it('does not modify strings without escaped characters', () => {
const input = 'Hello World';
expect(revertEscapedCharacters(input)).to.equal(input);
const text = 'Hello World';
expect(revertEscapedCharacters(text)).to.equal(text);
});

it('handles empty strings', () => {
expect(revertEscapedCharacters('')).to.equal('');
const text = '';
expect(revertEscapedCharacters(text)).to.equal(text);
});

it('handles multiple escaped carriage returns', () => {
const input = 'Hello\\rDear\\rWorld';
const expected = 'Hello\rDear\rWorld';
expect(revertEscapedCharacters(input)).to.equal(expected);
expect(
revertEscapedCharacters(
`Hello${CARRIAGE_RETURN_PLACEHOLDER}Dear${CARRIAGE_RETURN_PLACEHOLDER}World`,
),
).to.equal('Hello\rDear\rWorld');
});

it('only reverts escaped carriage returns', () => {
const input = 'Hello\\rWorld\\n'; // \n should not be affected
const expected = 'Hello\rWorld\\n';
expect(revertEscapedCharacters(input)).to.equal(expected);
// `\n` and `\right` should not be affected
expect(
revertEscapedCharacters(
`Hello${CARRIAGE_RETURN_PLACEHOLDER}World\\n\\right`,
),
).to.equal('Hello\rWorld\\n\\right');
});
});
});

0 comments on commit d69bc66

Please sign in to comment.