Skip to content

prefer-string-raw: Add support for template literals #2691

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

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

som-sm
Copy link
Contributor

@som-sm som-sm commented Jun 23, 2025

Closes #2653

@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from 28f9147 to b6932a5 Compare June 23, 2025 16:29
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from b6932a5 to b36a7a9 Compare June 23, 2025 17:06
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from 57d18d8 to ea61482 Compare June 24, 2025 08:22
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch 2 times, most recently from 1686254 to 8bbb723 Compare June 24, 2025 09:03
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from 8bbb723 to 3de2032 Compare June 24, 2025 09:06
@som-sm som-sm marked this pull request as ready for review June 24, 2025 09:56
@som-sm som-sm marked this pull request as draft June 24, 2025 15:49
@som-sm som-sm marked this pull request as ready for review June 24, 2025 16:17
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from de17322 to 30ed709 Compare June 26, 2025 13:45
@som-sm som-sm requested a review from fisker June 26, 2025 15:29
@som-sm som-sm force-pushed the feat/prefer-string-raw-template-literal-support branch from 0b09f48 to 6d81306 Compare June 27, 2025 07:22
@som-sm som-sm requested a review from fisker June 27, 2025 10:26
@fisker fisker marked this pull request as draft June 27, 2025 16:25
@fisker fisker marked this pull request as ready for review June 27, 2025 16:27
@fisker
Copy link
Collaborator

fisker commented Jun 27, 2025

I'm a little worried about how \r works, but I'm going to pretend I don't know it. It's quite tricky in template literals.

@som-sm
Copy link
Contributor Author

som-sm commented Jun 27, 2025

I'm a little worried about how \r works, but I'm going to pretend I don't know it. It's quite tricky in template literals.

Is this comment for this PR or the other one?

@fisker
Copy link
Collaborator

fisker commented Jun 27, 2025

This one.

@som-sm
Copy link
Contributor Author

som-sm commented Jun 27, 2025

This one.

Ok, I'm not sure if I follow, will \r behave any differently from other escaped characters?

If the string contains \r, then raw shouldn't be equal to cooked and it shouldn't complain.

@fisker
Copy link
Collaborator

fisker commented Jun 27, 2025

> require('espree').parse('`\r\r\n`',{ecmaVersion:'latest'}).body[0].expression.quasis[0]
Node {
  type: 'TemplateElement',
  start: 0,
  end: 5,
  value: { raw: '\n\n', cooked: '\n\n' },
  tail: true
}
> require('@typescript-eslint/typescript-estree').parse('`\r\r\n`').body[0].expression.quasis[0]
{
  type: 'TemplateElement',
  tail: true,
  value: { cooked: '\n\n', raw: '\r\r\n' }
}
> require('@babel/parser').parse('`\r\r\n`').program.body[0].expression.quasis[0]
Node {
  type: 'TemplateElement',
  start: 1,
  end: 4,
  loc: SourceLocation {
    start: Position { line: 1, column: 1, index: 1 },
    end: Position { line: 3, column: 0, index: 4 },
    filename: undefined,
    identifierName: undefined
  },
  value: { raw: '\n\n', cooked: '\n\n' },
  tail: true
}

As far as I know, it's not really clear in ESTree spec.

@som-sm
Copy link
Contributor Author

som-sm commented Jun 28, 2025

@fisker Looked into this a bit, here are my observations:

With espree and @babel/parser, there shouldn't be any issues since the raw and cooked values are the same.

The only thing is that in some situations we won't be preserving carriage returns, like this:

test.babel({
	valid: [],
	invalid: [
		// eslint-disable-next-line internal/no-test-only
		test.only({
			code: 'a = `a\\\\b \r\n`',
			output: 'a = String.raw`a\\b \r\n`',
			errors: 1,
		}),
	],
});

image

Is this a problem?


With the @typescript-eslint/typescript-estree parser, inputs like 'a = `a\\\\b \r\n`' should ideally be reported—but they won't, because the unescaped value wouldn't be equal to the cooked value. For example:

[
  {
    cooked: 'a\\b \n',
    raw: 'a\\\\b \r\n',
    unescapeBackslash: 'a\\b \r\n'
  }
]

Maybe we can fix this by replacing all \r and \r\n characters with \n in the raw value, like:

function normalizeCarriageReturn(text) {
	return text.replaceAll(/\r\n?/g, '\n');
}
if (
	(node.parent.type === 'TaggedTemplateExpression' && node.parent.quasi === node)
-	|| node.quasis.every(({value: {cooked, raw}}) => cooked === raw)
+	|| node.quasis.every(({value: {cooked, raw}}) => cooked === normalizeCarriageReturn(raw))
-	|| node.quasis.some(({value: {cooked, raw}}) => cooked.at(-1) === BACKSLASH || unescapeBackslash(raw) !== cooked)
+	|| node.quasis.some(({value: {cooked, raw}}) => cooked.at(-1) === BACKSLASH || unescapeBackslash(normalizeCarriageReturn(raw)) !== cooked)
) {
	return;
}

This would make cases like these be reported, but the carriage return would still not be preserved. So, you would get 'a = String.raw`a\\b \n`' for 'a = `a\\\\b \r\n`'

WDYT?

@fisker
Copy link
Collaborator

fisker commented Jun 28, 2025

I prefer just ignore it, if user complain, we can just blame typescript-eslint 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

prefer-string-raw: Support template literals
2 participants