From f4f9a36fa4ec8c7c0772a9d4bf9123a4aceae8bc Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Fri, 3 Apr 2026 10:08:44 +0300 Subject: [PATCH] fix(ecma): use scoped injections instead of combined Use `[string_fragment template_substitution]+` pattern for scoped injections per maintainer feedback, avoiding the cost of combined injections. This requires splitting alternations into separate patterns for the `fn()` and tagged template literal forms. Assisted-By: Claude Opus 4.6 (1M context) --- runtime/queries/ecma/injections.scm | 168 +++++++++++++++++++--------- 1 file changed, 113 insertions(+), 55 deletions(-) diff --git a/runtime/queries/ecma/injections.scm b/runtime/queries/ecma/injections.scm index 2aa7c2d21..d3b3a9340 100644 --- a/runtime/queries/ecma/injections.scm +++ b/runtime/queries/ecma/injections.scm @@ -5,51 +5,78 @@ ((comment) @injection.content (#set! injection.language "comment")) -; html(`...`), html`...`, sql(`...`), etc. +; html(`...`), sql(`...`), etc. (call_expression function: (identifier) @injection.language - arguments: [ - (arguments - (template_string - (string_fragment) @injection.content)) + arguments: (arguments (template_string - (string_fragment) @injection.content) - ] + [ + (string_fragment) @injection.content + (template_substitution) + ]+)) (#lua-match? @injection.language "^[a-zA-Z][a-zA-Z0-9]*$") - (#set! injection.combined) ; Languages excluded from auto-injection due to special rules ; - svg uses the html parser ; - css uses the styled parser (#not-any-of? @injection.language "svg" "css")) -; svg`...` or svg(`...`) +; html`...`, sql`...`, etc. +(call_expression + function: (identifier) @injection.language + arguments: (template_string + [ + (string_fragment) @injection.content + (template_substitution) + ]+) + (#lua-match? @injection.language "^[a-zA-Z][a-zA-Z0-9]*$") + (#not-any-of? @injection.language "svg" "css")) + +; svg(`...`) (call_expression function: (identifier) @_name (#eq? @_name "svg") - arguments: [ - (arguments - (template_string - (string_fragment) @injection.content)) + arguments: (arguments (template_string - (string_fragment) @injection.content) - ] - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+)) + (#set! injection.language "html")) + +; svg`...` +(call_expression + function: (identifier) @_name + (#eq? @_name "svg") + arguments: (template_string + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "html")) ; Vercel PostgreSQL -; foo.sql`...` or foo.sql(`...`) +; foo.sql(`...`) (call_expression function: (member_expression property: (property_identifier) @injection.language) - arguments: [ - (arguments - (template_string - (string_fragment) @injection.content)) + arguments: (arguments (template_string - (string_fragment) @injection.content) - ] - (#eq? @injection.language "sql") - (#set! injection.combined)) + [ + (string_fragment) @injection.content + (template_substitution) + ]+)) + (#eq? @injection.language "sql")) + +; foo.sql`...` +(call_expression + function: (member_expression + property: (property_identifier) @injection.language) + arguments: (template_string + [ + (string_fragment) @injection.content + (template_substitution) + ]+) + (#eq? @injection.language "sql")) ; Sanity CMS GROQ query ; defineQuery(`...`) @@ -58,30 +85,43 @@ (#eq? @_name "defineQuery") arguments: (arguments (template_string - (string_fragment) @injection.content)) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+)) (#set! injection.language "groq")) -; gql`...` or gql(`...`) +; gql(`...`) (call_expression function: (identifier) @_name (#eq? @_name "gql") - arguments: [ - (arguments - (template_string - (string_fragment) @injection.content)) + arguments: (arguments (template_string - (string_fragment) @injection.content) - ] - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+)) + (#set! injection.language "graphql")) + +; gql`...` +(call_expression + function: (identifier) @_name + (#eq? @_name "gql") + arguments: (template_string + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "graphql")) (call_expression function: (identifier) @_name (#eq? @_name "hbs") arguments: (template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "glimmer")) ; css``, keyframes`` @@ -89,8 +129,10 @@ function: (identifier) @_name (#any-of? @_name "css" "keyframes") arguments: (template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "styled")) ; styled.div`` @@ -99,8 +141,10 @@ object: (identifier) @_name (#eq? @_name "styled")) arguments: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "styled"))) ; styled(Component)`` @@ -109,8 +153,10 @@ function: (identifier) @_name (#eq? @_name "styled")) arguments: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "styled"))) ; styled.div.attrs({ prop: "foo" })`` @@ -121,8 +167,10 @@ object: (identifier) @_name (#eq? @_name "styled")))) arguments: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "styled"))) ; styled(Component).attrs({ prop: "foo" })`` @@ -133,8 +181,10 @@ function: (identifier) @_name (#eq? @_name "styled")))) arguments: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "styled"))) ((regex_pattern) @injection.content @@ -156,8 +206,10 @@ property: (property_identifier) @_prop (#any-of? @_prop "outerHTML" "innerHTML")) right: (template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "html")) ; el.innerHTML = '' @@ -183,8 +235,10 @@ key: ((property_identifier) @_prop (#eq? @_prop "template")) value: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "angular"))))))) ; @Component({ @@ -201,8 +255,10 @@ (#eq? @_prop "styles")) value: (array ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "css")))))))) ; @Component({ @@ -218,6 +274,8 @@ key: ((property_identifier) @_prop (#eq? @_prop "styles")) value: ((template_string - (string_fragment) @injection.content) - (#set! injection.combined) + [ + (string_fragment) @injection.content + (template_substitution) + ]+) (#set! injection.language "css")))))))