Skip to content

Commit

Permalink
Document changes to expressions (#527)
Browse files Browse the repository at this point in the history
* Add release note

* Add docs

* Add default value and trim

* Add multiline suggestion to error

* Join using semicolon only

* Revert removing line break

* Improve code comments

* Fix typo

* Fix

* Use button element

* Build
  • Loading branch information
bencroker authored Jan 26, 2025
1 parent cf7807b commit 46773b5
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Changed

- Changed the `data-on-interval` attribute to accept a `__duration` modifier instead of a `__delay` modifier ([#513](https://github.com/starfederation/datastar/issues/513)).
- Changed the parsing of Datastar expressions so that only semicolons can be used to explicitly indicate a statement delimiter ([#525](https://github.com/starfederation/datastar/issues/525)).

### Fixed

Expand Down
10 changes: 5 additions & 5 deletions bundles/datastar-core.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions bundles/datastar-core.js.map

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions bundles/datastar.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions bundles/datastar.js.map

Large diffs are not rendered by default.

22 changes: 13 additions & 9 deletions library/src/engine/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ export class Engine {
ctx: RuntimeContext,
...argNames: string[]
): RuntimeExpressionFunction {
let userExpression = ''

// This regex allows Datastar expressions to support nested
// regex and strings that contain ; and/or \n without breaking.
// regex and strings that contain ; without breaking.
//
// Each of these regex defines a block type we want to match
// (importantly we ignore the content within these blocks):
Expand All @@ -212,20 +214,22 @@ export class Engine {
// ticks `(\\`|[^`])*`
//
// We also want to match the non delimiter part of statements
// note we only support ; statement delimiters and not \n :
// note we only support ; statement delimiters:
//
// [^;]
//
const statementRe = /(\/(\\\/|[^\/])*\/|"(\\"|[^\"])*"|'(\\'|[^'])*'|`(\\`|[^`])*`|[^;])+/gm
const stmts = ctx.value.trim().match(statementRe)
const lastIdx = stmts.length - 1
const last = stmts[lastIdx]
if (!last.startsWith('return')) {
stmts[lastIdx] = `return (${last});`
const statements = ctx.value.trim().match(statementRe)
if (statements) {
const lastIdx = statements.length - 1
const last = statements[lastIdx].trim()
if (!last.startsWith('return')) {
statements[lastIdx] = `return (${last});`
}
userExpression = statements.join(';\n')
}
let userExpression = stmts.join(';\n')

// Ingore any escaped values
// Ignore any escaped values
const escaped = new Map<string, string>()
const escapeRe = new RegExp(`(?:${DSP})(.*?)(?:${DSS})`, 'gm')
for (const match of userExpression.matchAll(escapeRe)) {
Expand Down
4 changes: 2 additions & 2 deletions sdk/go/consts.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 18 additions & 2 deletions site/routes_errors_runtime.templ
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ templ CustomValidityInvalidExpression(name string, info *RuntimeErrorInfo) {

templ ExecuteExpression(name string, info *RuntimeErrorInfo) {
{{
var isSignalRe = regexp.MustCompile("^(\\$\\S*) is not defined")
var isSignalRe = regexp.MustCompile(`^(\$\S*) is not defined`)
isSignalMatches := isSignalRe.FindAllStringSubmatch(info.Error, -1)
}}
@RuntimeErrorView(name, info) {
Expand Down Expand Up @@ -110,7 +110,7 @@ templ ExecuteExpression(name string, info *RuntimeErrorInfo) {
</div>`)
} else {
{{
var isUndefinedStringRe = regexp.MustCompile("^(\\S*) is not defined")
var isUndefinedStringRe = regexp.MustCompile(`^(\S*) is not defined`)
isStringMatches := isUndefinedStringRe.FindAllStringSubmatch(info.Error, -1)
}}
if len(isStringMatches) > 0 {
Expand All @@ -128,8 +128,24 @@ templ ExecuteExpression(name string, info *RuntimeErrorInfo) {
}

templ GenerateExpression(name string, info *RuntimeErrorInfo) {
{{

var isMultilineReturnRe = regexp.MustCompile(`return \([\s\S]*\n[\s\S]*\);`)
isMultilineReturnMatches := isMultilineReturnRe.FindAllStringSubmatch(info.Expression.FnContent, -1)
}}
@RuntimeErrorView(name, info) {
<p>A valid expression could not be generated.</p>
if len(isMultilineReturnMatches) > 0 {
<p>Multiple statements can be used in a single expression by separating them with a semicolon Expressions may span multiple lines, but a semicolon must be used to separate statements.</p>
@sampleCode("Example", "html", `<div data-signals-foo="1">
<div data-on-click="
$foo++;
@post('/endpoint')
"></div>
</div>`)
} else {
<pre>{ info.Expression.FnContent }</pre>
}
@expressionDocs()
}
}
Expand Down
19 changes: 19 additions & 0 deletions site/static/md/guide/datastar_expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ The following example is valid because both `$foo.bar` and `$baz` are signals.

Note that `data-*` attributes are evaluated in the order they appear in the DOM, so the `data-text` attributes must come _after_ the `data-signals-*` attributes in the example above. See the [attribute plugins reference](/reference/attribute_plugins) for more information.

Multiple statements can be used in a single expression by separating them with a semicolon.

```html
<div data-signals-foo="1">
<button data-on-click="$foo++; @post('/endpoint')"></button>
</div>
```

Expressions may span multiple lines, but a semicolon must be used to separate statements.

```html
<div data-signals-foo="1">
<button data-on-click="
$foo++;
@post('/endpoint')
"></button>
</div>
```

### Security

When using a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP), `unsafe-eval` must be allowed for scripts, since Datastar evaluates expressions inline.
Expand Down

0 comments on commit 46773b5

Please sign in to comment.