|
| 1 | +--- |
| 2 | +title: 'v3 Release Notes' |
| 3 | +description: 'Get to know details about the Textwire v3 release by reading the complete release notes' |
| 4 | +authors: [serhiicho] |
| 5 | +tags: [release] |
| 6 | +--- |
| 7 | + |
| 8 | +Textwire v3 is one of the most important releases, introducing several significant features and improvements. This release includes the introduction of the `global` variable object, a new [defined()](/docs/v3/functions/global#defined) function ([#56](https://github.com/textwire/textwire/issues/56)), the ability to register custom functions for the OBJECT type, and improvements to error handling, performance ([#61](https://github.com/textwire/textwire/issues/61) [#60](https://github.com/textwire/textwire/issues/60) [#59](https://github.com/textwire/textwire/issues/59)), and usability [#35](https://github.com/textwire/textwire/issues/35). |
| 9 | + |
| 10 | +<!-- truncate --> |
| 11 | + |
| 12 | +If you are transitioning from Textwire v2, you can follow [this guide](/docs/v3/upgrade) for all the instructions. v3 contains several breaking changes, so make sure you don't miss any parts of the guide. |
| 13 | + |
| 14 | +### Table of Contents |
| 15 | + |
| 16 | +- [New Features](#new-features) |
| 17 | +- [Improvements](#improvements) |
| 18 | +- [Bug Fixes](#bug-fixes) |
| 19 | +- [Breaking Changes](#breaking-changes) |
| 20 | + |
| 21 | +## New Features |
| 22 | + |
| 23 | +Textwire v3 brings several new features to the language. Let's explore these features, starting with a new concept in Textwire - global functions. |
| 24 | + |
| 25 | +### 1. Function `defined` |
| 26 | + |
| 27 | +Textwire v3 introduces the global function [defined()](/docs/v3/functions/global#defined) that returns a boolean value. This function should be used only with variables and allows you to check if a variable is defined to prevent Textwire from creating an error. |
| 28 | + |
| 29 | +```textwire title="components/header.tw" |
| 30 | +<header class="header"> |
| 31 | + <h2>{{ page.title }}</h2> |
| 32 | + <p>{{ page.desc }}</p> |
| 33 | +
|
| 34 | + @if(defined(user)) |
| 35 | + <small>The user is {{ user.name }}</small> |
| 36 | + @end |
| 37 | +</header> |
| 38 | +``` |
| 39 | + |
| 40 | +Here are the commits [0a19b](https://github.com/textwire/textwire/commit/0a19b5b5d6363a2ee9c9827df25026ffab5c2e1e), [f1d3f](https://github.com/textwire/textwire/commit/f1d3fdc0736dd0892aeacfa28b5156cca0c78ac3), [264e7](https://github.com/textwire/textwire/commit/264e7ffc37a1d3bf6344fb99530be2f2b89c2081). |
| 41 | + |
| 42 | +### 2. Global Data |
| 43 | + |
| 44 | +In v3 you can now pass values from your Go code to any Textwire template using the global object. Here is an example of passing some values: |
| 45 | + |
| 46 | +```go title="main.go" |
| 47 | +import ( |
| 48 | + "os" |
| 49 | + "github.com/textwire/textwire/v3" |
| 50 | + "github.com/textwire/textwire/v3/config" |
| 51 | +) |
| 52 | + |
| 53 | +tpl, err = textwire.NewTemplate(&config.Config{ |
| 54 | + DebugMode: true, |
| 55 | + GlobalData: map[string]any{ |
| 56 | + "env": os.Getenv("APP_ENV"), |
| 57 | + }, |
| 58 | +}) |
| 59 | +``` |
| 60 | + |
| 61 | +In your templates, you can access global object like this: |
| 62 | + |
| 63 | +```textwire title="home.tw" |
| 64 | +@if (global.env == "development") |
| 65 | + <p>You are currently in development mode</p> |
| 66 | +@end |
| 67 | +``` |
| 68 | + |
| 69 | +Read more about [Global Data](/docs/v3/guides/configurations#global-data) in our docs, or look at the [commit](https://github.com/textwire/textwire/commit/f156f3fd2175f925652d462d75dc843a396de702). |
| 70 | + |
| 71 | +### 3. Custom Functions for Object Type |
| 72 | + |
| 73 | +In Textwire v2 you could define [custom functions](/docs/v3/guides/custom-functions) for all types except objects; in v3 you can now do that. This was added with [this commit](https://github.com/textwire/textwire/commit/a225ccacaf0fc9ca62365ebfe7e715de39b067af). Here is a simple example: |
| 74 | + |
| 75 | +```go |
| 76 | +err := textwire.RegisterObjFunc("_addProp", func(obj map[string]any, args ...any) any { |
| 77 | + key := args[0].(string) // first arg |
| 78 | + value := args[1] // second arg |
| 79 | + obj[key] = value |
| 80 | + return obj |
| 81 | +}) |
| 82 | +``` |
| 83 | + |
| 84 | +You can now use it in Textwire: |
| 85 | + |
| 86 | +```go |
| 87 | +input := `{{ obj = {name: "Anna"}; obj = obj._addProp("age", 25); obj.age }}` |
| 88 | +result, err := textwire.EvaluateString(input, nil) |
| 89 | +// Result: "25" |
| 90 | +``` |
| 91 | + |
| 92 | +### 4. Embed Templates into Binary |
| 93 | + |
| 94 | +Now, in Textwire v3 you can use Go's embedded package to embed Textwire template files into a final, compiled binary. You can read about how to use this functionality in [our docs](/docs/v3/guides/template-embedding). But it looks something like this: |
| 95 | + |
| 96 | +```go title="main.go" showLineNumbers |
| 97 | +package main |
| 98 | + |
| 99 | +import ( |
| 100 | + "embed" |
| 101 | + |
| 102 | + "github.com/textwire/textwire/v3" |
| 103 | + "github.com/textwire/textwire/v3/config" |
| 104 | +) |
| 105 | + |
| 106 | +// highlight-start |
| 107 | +//go:embed templates/* |
| 108 | +var templateFS embed.FS |
| 109 | +// highlight-end |
| 110 | + |
| 111 | +func main() { |
| 112 | + tpl, err := textwire.NewTemplate(&config.Config{ |
| 113 | + // highlight-next-line |
| 114 | + TemplateFS: templateFS, |
| 115 | + }) |
| 116 | + |
| 117 | + // other logic here ... |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +## Improvements |
| 122 | + |
| 123 | +### 1. Error Handling |
| 124 | + |
| 125 | +- Improve error handling with [this commit](https://github.com/textwire/textwire/commit/d9442c5d567d788652c03fb8efa6125c93ee5843) when trying to use `@use`, `@insert`, `@reserve` or `@component` directives in simple `EvaluateString` or `EvaluateFile` function calls. These directives are only allowed inside template files with `textwire.NewTemplate`. |
| 126 | +- Now, you'll get a clear error message like `@use, @insert, @reserve, @component only allowed in templates` if you try to use them. Previously, the error wasn't clear. |
| 127 | +- Improved all error messages in [this commit](https://github.com/textwire/textwire/commit/e6b0935af2d7de0469733e12028fc349564584c6) to make them clearer and more straightforward. |
| 128 | +- You'll get a proper error instead of panic when you try to use `@each(item in false)` directive on non-array type. |
| 129 | +- You'll get a clear error when using 2 or more `@use` statements in the same template. |
| 130 | + |
| 131 | +There are much more improvements to error handling in this release, so make sure to [upgrade](/docs/v3/upgrade) to version 3. |
| 132 | + |
| 133 | +### 2. Memory Performance |
| 134 | + |
| 135 | +Improve memory and performance with optimized data structures and reduced memory allocations. Here are the optimizations that you can expect in Textwire v3: |
| 136 | + |
| 137 | +| Improved target | Speed | Memory usage | Allocations | |
| 138 | +| -------------------------------------------------- | ------------------- | ----------------- | ------------------ | |
| 139 | +| Function [arr.join()](/docs/v3/functions/arr#join) | ⚡ **18.5× faster** | 💾 **97.8% less** | 📉 **33% fewer** | |
| 140 | +| Tokenizing (lexing) directives | ⚡ **1.24× faster** | 💾 **46.9% less** | 📉 **84.9% fewer** | |
| 141 | +| Parsed AST evaluation | **No change** | 💾 **9.3% less** | 📉 **2.5% fewer** | |
| 142 | + |
| 143 | +Here is a GitHub [issue](https://github.com/textwire/textwire/issues/59) for `array.join()` if you are interested. |
| 144 | + |
| 145 | +## Bug Fixes |
| 146 | + |
| 147 | +Like any software, Textwire has its share of bugs. We found and fixed several bugs in this release. You can find the list of fixed bugs below. |
| 148 | + |
| 149 | +### 1. Using Component Statement inside Layout |
| 150 | + |
| 151 | +Fixed bug where you couldn't use `@component` directive inside of a layout file like this: |
| 152 | + |
| 153 | +```textwire |
| 154 | +<!DOCTYPE html> |
| 155 | +<html lang="en"> |
| 156 | +<head> |
| 157 | + <meta charset="UTF-8"> |
| 158 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 159 | + <title>@reserve('title')</title> |
| 160 | + <meta name="description" content="@reserve('description')"> |
| 161 | +</head> |
| 162 | +<body> |
| 163 | +@component('~navbar') {{-- using components/navbar here --}} |
| 164 | +<main class="container mx-auto max-w-200"> |
| 165 | +@reserve('content') |
| 166 | +</main> |
| 167 | +</body> |
| 168 | +</html> |
| 169 | +``` |
| 170 | + |
| 171 | +You also couldn't use `@component` inside of other components. Now, it all has been fixed. |
| 172 | + |
| 173 | +### 2. Incorrect File Path |
| 174 | + |
| 175 | +Fixed incorrect file path in error messages when an error occurs inside the `@insert` directive. It used to point to the wrong file. |
| 176 | + |
| 177 | +### 3. Function `contains()` |
| 178 | + |
| 179 | +- Fixed `contains` function for strings, `{{ !"aaa".contains("a") }}` now returns correct result. |
| 180 | +- Fixed `contains` function for arrays, `{{ ![{}, 21].contains({age: 21}) }}` now returns correct result. |
| 181 | + They both used to work incorrect because of incorrect precidence, which was fixed. |
| 182 | + |
| 183 | +### 4. Replace Panic with Error |
| 184 | + |
| 185 | +Now you will get a proper error when trying to access propery on non object type like `{{ "str".nice }}`. Before, in Textwire v2 you would get a panic with weird error message and long stacktrace. |
| 186 | + |
| 187 | +## Breaking Changes |
| 188 | + |
| 189 | +Textwire v3 introduces several breaking changes. We're implementing these changes now rather than later, as it's better to make significant updates when the user base is smaller. |
| 190 | + |
| 191 | +This transition would be much harder with tens of thousands of users than with hundreds. Thank you for choosing Textwire - we'll provide the least painful transition experience with our [detailed upgrade guide](/docs/v3/upgrade). |
| 192 | + |
| 193 | +:::important |
| 194 | +I'll discuss each breaking change briefly because they are already explained in the [Upgrade Guide](/docs/v3/upgrade), so there's no need to repeat that detailed information here. |
| 195 | +::: |
| 196 | + |
| 197 | +### 1. Custom Functions Return Type |
| 198 | + |
| 199 | +When you define a custom function, it now returns the type `any`. If you have any registered custom functions, make sure to change their return type to `any`. |
| 200 | + |
| 201 | +### 2. Reserved Variable Name |
| 202 | + |
| 203 | +Variable `global` is now reserved, you cannot use this name for your variables. |
| 204 | + |
| 205 | +### 3. Precedence Fix |
| 206 | + |
| 207 | +Fixed precedence for prefix expressions and function call expressions. In Textwire v3, function calls now have higher precedence over prefix expressions. Instead of `((!var).func())`, we now have `(!(var.func()))`. |
| 208 | + |
| 209 | +### 4. Default Extension Change |
| 210 | + |
| 211 | +Changed the default file extension from `.tw.html` to `.tw`. If you still want to support `.tw.html`, add the field `TemplateExt: ".tw.html"` to your configuration in `NewTemplate` or `Configure`. This will behave the same way as in Textwire v2. |
| 212 | + |
| 213 | +### 5. Minimal Go Version |
| 214 | + |
| 215 | +In Textwire v3, the minimum supported Go version is now `1.25.0`. In Textwire v2, it was `1.22.0`. Upgrade your project to the latest version to use Textwire v3. |
| 216 | + |
| 217 | +### 6. Components Scope Fix |
| 218 | + |
| 219 | +Components in **Textwire v2** would pass variables to their children automatially without manual passing. It was a bug. In **Textwire v3** each component has its scope. You need to pass data manually: |
| 220 | + |
| 221 | +```diff |
| 222 | +{{ name = "Anna" }} |
| 223 | + |
| 224 | +- @component('user') |
| 225 | ++ @component('user', { name }) |
| 226 | +``` |
| 227 | + |
| 228 | +### 7. Variable Leak Fix |
| 229 | + |
| 230 | +Fixed variable leak from template to layout non-explicitly. In Textwire v2, if you had a variable in your template, it would be accessible in your layout without passing it explicitly. In Textwire v3, this is not available anymore. |
| 231 | + |
| 232 | +Use the [Global Data](/docs/v3/guides/configurations#global-data) to pass variables into all of your Textwire files. |
0 commit comments