diff --git a/CHANGELOG.md b/CHANGELOG.md index dd49610..5c8f047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # esrap changelog +## 1.4.1 + +### Patch Changes + +- 10c6156: fix: escape newlines when quoting strings + ## 1.4.0 ### Minor Changes diff --git a/package.json b/package.json index b6d2b8f..fb6cc1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "esrap", - "version": "1.4.0", + "version": "1.4.1", "description": "Parse in reverse", "repository": { "type": "git", diff --git a/src/handlers.js b/src/handlers.js index 70371bb..c504fcc 100644 --- a/src/handlers.js +++ b/src/handlers.js @@ -115,7 +115,26 @@ function prepend_comments(comments, state, newlines) { * @param {'\'' | '"'} char */ function quote(string, char) { - return char + string.replaceAll(char, '\\' + char) + char; + let out = char; + let escaped = false; + + for (const c of string) { + if (escaped) { + out += c; + escaped = false; + } else if (c === '\\') { + out += '\\\\'; + escaped = true; + } else if (c === char) { + out += '\\' + c; + } else if (c === '\n') { + out += '\\n'; + } else { + out += c; + } + } + + return out + char; } const OPERATOR_PRECEDENCE = { diff --git a/test/quotes.test.js b/test/quotes.test.js index a8c8590..c8e1372 100644 --- a/test/quotes.test.js +++ b/test/quotes.test.js @@ -61,3 +61,35 @@ test('escape double quotes if present in string literal', () => { expect(code).toMatchInlineSnapshot(`"const foo = "b\\"ar";"`); }); + +test('escapes new lines', () => { + const ast = load('const str = "a\\nb"'); + clean(ast); + const code = print(ast).code; + + expect(code).toMatchInlineSnapshot(`"const str = 'a\\nb';"`); +}); + +test('escapes escape characters', () => { + const ast = load('const str = "a\\\\nb"'); + clean(ast); + const code = print(ast).code; + + expect(code).toMatchInlineSnapshot(`"const str = 'a\\\\nb';"`); +}); + +test('does not escape already-escaped single quotes', () => { + const ast = load(`const str = 'a\\'b'`); + clean(ast); + const code = print(ast).code; + + expect(code).toMatchInlineSnapshot(`"const str = 'a\\'b';"`); +}); + +test('does not escape already-escaped double quotes', () => { + const ast = load('const str = "a\\"b"'); + clean(ast); + const code = print(ast).code; + + expect(code).toMatchInlineSnapshot(`"const str = 'a"b';"`); +});