-
-
Notifications
You must be signed in to change notification settings - Fork 87
fix: unpaired apostrophe in JavaScript comments #370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ccd2f84
b335b27
038fd66
c29c0ac
bc1e1d1
34d8d6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ const singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g; | |
|
|
||
| const doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g; | ||
|
|
||
| const lineTerminatorReg = /(?:\r\n|[\n\r\u2028\u2029])/g; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The regex itself seems correct to me. There might be a way to use the The regex consumes CRLF when it is found, which is potentially helpful, because it'll move the search needle entirely past the linebreak to the following line's content.. although makes the code slightly less platform-agnostic. |
||
|
|
||
| /** Escape special regular expression characters inside a string */ | ||
|
|
||
| function escapeRegExp(string: string) { | ||
|
|
@@ -109,7 +111,9 @@ export function parse(this: Eta, str: string): Array<AstObject> { | |
| ); | ||
|
|
||
| const parseCloseReg = new RegExp( | ||
| "'|\"|`|\\/\\*|(\\s*(-|_)?" + escapeRegExp(config.tags[1]) + ")", | ||
| "'|\"|`|\\/\\*|(\\s*(-|_)?" + | ||
| escapeRegExp(config.tags[1]) + | ||
| ")|\\/\\/", | ||
| "g", | ||
| ); | ||
|
|
||
|
|
@@ -161,6 +165,14 @@ export function parse(this: Eta, str: string): Array<AstObject> { | |
| ParseErr("unclosed comment", str, closeTag.index); | ||
| } | ||
| parseCloseReg.lastIndex = commentCloseInd; | ||
| } else if (char === "//") { | ||
| lineTerminatorReg.lastIndex = parseCloseReg.lastIndex; | ||
| const match = lineTerminatorReg.exec(str); | ||
| if (match) { | ||
| parseCloseReg.lastIndex = match.index + match[0].length; | ||
| } else { | ||
| parseCloseReg.lastIndex = str.length; | ||
| } | ||
|
rtritto marked this conversation as resolved.
|
||
| } else if (char === "'") { | ||
| singleQuoteReg.lastIndex = closeTag.index; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,22 @@ describe("parse test", () => { | |
| ]); | ||
| }); | ||
|
|
||
| it("works with unpaired apostrophe in single-line comment", () => { | ||
| const buff = eta.parse("hi <% // comment with unpaired apostrophe' \n %>"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more request for a test case: please could we add a (negative) test case where a single-line comment extends all the way to the end of the template? In other words, something like: This should raise an The reason I'm asking: I think it might be possible to |
||
| expect(buff).toEqual([ | ||
| "hi ", | ||
| { val: "// comment with unpaired apostrophe' \n", t: "e" }, | ||
| ]); | ||
| }); | ||
|
|
||
| it("works with unpaired apostrophe in multiline comment", () => { | ||
| const buff = eta.parse("hi <% /* comment with unpaired apostrophe' */ %>"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rtritto your fix works for double-quotes in addition to apostrophes, and I think that might be worth demonstrating too. Another set of scenarios I'll look into is cases where there are mixed |
||
| expect(buff).toEqual([ | ||
| "hi ", | ||
| { val: "/* comment with unpaired apostrophe' */", t: "e" }, | ||
| ]); | ||
| }); | ||
|
|
||
| it("parses with simple template literal", () => { | ||
| // biome-ignore lint/suspicious/noTemplateCurlyInString: intentional | ||
| const buff = eta.parse("hi <%= `template %> ${value}` %>"); | ||
|
|
@@ -114,4 +130,32 @@ describe("parse test", () => { | |
| <%= /* %> | ||
| ^`); | ||
| }); | ||
|
|
||
| it("handles alternative closing tags properly, rather than confusing them with comments", () => { | ||
| const originalEta = new Eta({ tags: ["{{", "//"] }); | ||
| const buff = originalEta.parse("{{= it.x//"); | ||
| expect(buff).toEqual([{ val: "it.x", t: "i" }]); | ||
| const originalEta2 = new Eta({ tags: ["{{", "//}}"] }); | ||
| const buff2 = originalEta2.parse("{{= it.x//}}"); | ||
| expect(buff2).toEqual([{ val: "it.x", t: "i" }]); | ||
| }); | ||
|
|
||
| it("handles various line termination characters in single-line comments", () => { | ||
| const cases = ["\r", "\r\n", "\u2028", "\u2029", "\n"]; | ||
| for (const terminator of cases) { | ||
| const buff = eta.parse(`hi <% // comment ending with terminator${terminator} %>`); | ||
| expect(buff).toEqual([ | ||
| "hi ", | ||
| { val: `// comment ending with terminator${terminator}`, t: "e" }, | ||
| ]); | ||
| } | ||
| }); | ||
|
|
||
| it("handles a sequence looking like close tag inside single line comment", () => { | ||
| const buff = eta.parse("hi <% // comment %> with close tag\n %>"); | ||
| expect(buff).toEqual([ | ||
| "hi ", | ||
| { val: "// comment %> with close tag\n", t: "e" }, | ||
| ]); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me think that we might be missing the
U+2028/U+2029line terminators from the end-quote regex patterns (singleQuoteReg,doubleQuoteReg). I haven't explored this yet but would like to.