Skip to content

feat: Support template literals in console.log - #3352

Open
Aquel32 wants to merge 3 commits into
software-mansion:mainfrom
Aquel32:feat/template-literal
Open

Aquel32 wants to merge 3 commits into
software-mansion:mainfrom
Aquel32:feat/template-literal

Conversation

@Aquel32

@Aquel32 Aquel32 commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Closes #1700

  • Adds template literal expression
  • Removes eslint rule
  • Tests and examples included

Copilot AI balanced review requested due to automatic review settings November 10, 2026 14:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature works for the happy paths: console.log with a template literal, with runtime expressions, and with a constant expression all resolve correctly. The existing tests pass, and I added local checks for vec3f and string expressions, which also work.

Two things I would fix before merging:

  1. The cookQuasis fallback is both unreachable and wrong. cooked is only null for tagged templates with invalid escapes, and the transpiler does not handle TaggedTemplateExpression, so this code path is never reached. If it ever becomes reachable, its replacement order is wrong: it cooks \n before \\, so \\n becomes \ + newline, and the final .replaceAll(/\\/g, '\\') is a no-op. A proper cook needs to be a single left-to-right pass.

  2. Removing the whole TemplateLiteral ESLint rule is broader than the feature. The generator only supports template literals as direct arguments to console.log (and other log ops). Tagged templates (tag\...`) still fail at transpile time with Unsupported JS functionality: TaggedTemplateExpression, and non-log template literals (``const s = \hello`) throw a WGSL type error. Keeping the rule for tagged templates, or narrowing it to console.log` arguments, would keep the early lint signal.

I also found two smaller issues: tseynit's stringifyExpression does not escape cooked quasis, so a template literal containing a backtick or ${ produces invalid JS in error messages; and nested template literals are not flattened by generateLog's flatMap, so the log metadata stores a TemplateLiteralExpression object instead of the inner text. The inline comments below cover the details.


Note: 1 inline comment(s) dropped because they did not anchor to lines inside the PR diff:

  • packages/eslint-plugin/src/rules/noUnsupportedSyntax.ts:63 (RIGHT) — line 63 (RIGHT) is not inside a diff hunk

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

return ident[1];
}

function cookQuasis(raw: string): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback is unreachable for the supported syntax: cooked is only null for tagged templates with invalid escapes, and TaggedTemplateExpression is not handled by the transpiler. If it is ever reached, it is also wrong: \ is cooked before \\\\, so \ becomes \ + newline, and the final .replaceAll(/\\/g, '\\') is a no-op. Consider removing the fallback or implementing a proper single-pass cook.

return 'TemplateLiteralExpression';
}

[$comptimeValueOf](): ComptimeValue {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$comptimeValueOf returns the array of elements, not the template's string value. For a comptime value, the template literal should evaluate to its concatenated string (e.g. a${1}b -> a1b), so asComptime on this object returns a misleading array. This is currently only used in the console.log path, but the wrong comptime value is a latent trap if the expression is reused elsewhere.

Comment on lines +529 to +531
args.flatMap((arg) =>
arg.value instanceof TemplateLiteralExpression ? arg.value.elements : [arg],
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only flattens one level. A nested template literal such as console.log(`outer ${`inner ${4}`} end`) leaves the inner TemplateLiteralExpression in the log args. I verified that logIdToMeta then contains a TemplateLiteralExpression object instead of the inner text, and the runtime would print that object. Consider making the flattening recursive.

return 'null';
}

if (node[0] === NODE.templateLiteral) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quasis are already cooked text, so a backtick or ${ in a quasi produces invalid JS in the stringified expression. I verified stringifyNode([108, ['a`b', 'c'], [[5, '4']]]) yields `a`b${4}c`. Use JSON.stringify or re-escape the quasis when building the output.

This branch has not been deployed

No deployments
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.

feat: Support template literals in console.log

2 participants