Skip to content

Commit

Permalink
feat: function called as tagged template literal is reactively called (
Browse files Browse the repository at this point in the history
…#12692)

* feat: function called as tagged template literal is reactively called

Co-authored-by: Oscar Dominguez <[email protected]>

* chore: re-organize import of visitors

* simplify

---------

Co-authored-by: Oscar Dominguez <[email protected]>
Co-authored-by: Rich Harris <[email protected]>
  • Loading branch information
3 people authored Aug 2, 2024
1 parent e4e66e2 commit 3286617
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-owls-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

feat: function called as tagged template literal is reactively called
2 changes: 2 additions & 0 deletions packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { SvelteElement } from './visitors/SvelteElement.js';
import { SvelteFragment } from './visitors/SvelteFragment.js';
import { SvelteHead } from './visitors/SvelteHead.js';
import { SvelteSelf } from './visitors/SvelteSelf.js';
import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js';
import { Text } from './visitors/Text.js';
import { TitleElement } from './visitors/TitleElement.js';
import { UpdateExpression } from './visitors/UpdateExpression.js';
Expand Down Expand Up @@ -160,6 +161,7 @@ const visitors = {
SvelteFragment,
SvelteComponent,
SvelteSelf,
TaggedTemplateExpression,
Text,
TitleElement,
UpdateExpression,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { get_rune } from '../../scope.js';
import * as e from '../../../errors.js';
import { get_parent, unwrap_optional } from '../../../utils/ast.js';
import { is_safe_identifier } from './shared/utils.js';
import { is_known_safe_call, is_safe_identifier } from './shared/utils.js';

/**
* @param {CallExpression} node
Expand Down Expand Up @@ -150,7 +150,7 @@ export function CallExpression(node, context) {
break;
}

if (context.state.expression && !is_known_safe_call(node, context)) {
if (context.state.expression && !is_known_safe_call(node.callee, context)) {
context.state.expression.has_call = true;
context.state.expression.has_state = true;
}
Expand Down Expand Up @@ -182,28 +182,3 @@ export function CallExpression(node, context) {
context.next();
}
}

/**
* @param {CallExpression} node
* @param {Context} context
* @returns {boolean}
*/
function is_known_safe_call(node, context) {
const callee = node.callee;

// String / Number / BigInt / Boolean casting calls
if (callee.type === 'Identifier') {
const name = callee.name;
const binding = context.state.scope.get(name);
if (
binding === null &&
(name === 'BigInt' || name === 'String' || name === 'Number' || name === 'Boolean')
) {
return true;
}
}

// TODO add more cases

return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @import { TaggedTemplateExpression, VariableDeclarator } from 'estree' */
/** @import { Context } from '../types' */
import { is_known_safe_call } from './shared/utils.js';

/**
* @param {TaggedTemplateExpression} node
* @param {Context} context
*/
export function TaggedTemplateExpression(node, context) {
if (context.state.expression && !is_known_safe_call(node.tag, context)) {
context.state.expression.has_call = true;
context.state.expression.has_state = true;
}

if (node.tag.type === 'Identifier') {
const binding = context.state.scope.get(node.tag.name);

if (binding !== null) {
binding.is_called = true;
}
}
context.next();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @import { AssignmentExpression, Expression, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { AssignmentExpression, CallExpression, Expression, Pattern, PrivateIdentifier, Super, TaggedTemplateExpression, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { Fragment } from '#compiler' */
/** @import { AnalysisState, Context } from '../../types' */
/** @import { Scope } from '../../../scope' */
Expand Down Expand Up @@ -165,3 +165,26 @@ export function is_safe_identifier(expression, scope) {
binding.kind !== 'rest_prop'
);
}

/**
* @param {Expression | Super} callee
* @param {Context} context
* @returns {boolean}
*/
export function is_known_safe_call(callee, context) {
// String / Number / BigInt / Boolean casting calls
if (callee.type === 'Identifier') {
const name = callee.name;
const binding = context.state.scope.get(name);
if (
binding === null &&
(name === 'BigInt' || name === 'String' || name === 'Number' || name === 'Boolean')
) {
return true;
}
}

// TODO add more cases

return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';

export default test({
mode: ['client'],
async test({ assert, target, ok }) {
const button = target.querySelector('button');

assert.htmlEqual(target.innerHTML, `0 <button></button>`);

flushSync(() => {
button?.click();
});

assert.htmlEqual(target.innerHTML, `1 <button></button>`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
let count = $state(0);
function showCount() {
return count;
}
</script>

{showCount``} <button onclick={() => count++}></button>

0 comments on commit 3286617

Please sign in to comment.