Skip to content

Commit 6398a14

Browse files
authored
Merge pull request #11 from textwire/v3
V3
2 parents 5acebcc + ba9f7a5 commit 6398a14

63 files changed

Lines changed: 3373 additions & 94 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
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.

blog/authors.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
serhiicho:
22
name: Serhii Cho
3-
title: Full Stack Engineer
3+
title: Software Engineer
44
url: https://serhiicho.com
55
image_url: https://github.com/SerhiiCho.png
66
page: true
77
socials:
88
x: SerhiiCho
99
github: SerhiiCho
10-
linkedin: serhiicho

docusaurus.config.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ const config: Config = {
3939
label: 'v2',
4040
path: 'v2',
4141
},
42+
v3: {
43+
label: 'v3',
44+
path: 'v3',
45+
},
4246
},
43-
onlyIncludeVersions: ['v1', 'v2'],
47+
onlyIncludeVersions: ['v1', 'v2', 'v3'],
4448
},
4549
blog: {
4650
showReadingTime: true,
@@ -74,7 +78,7 @@ const config: Config = {
7478
label: 'Docs',
7579
},
7680
{ to: '/blog', label: 'Blog', position: 'left' },
77-
{ to: '/community', label: 'Community', position: 'left' },
81+
{ to: '/community', label: '♥️ Support', position: 'left' },
7882
{
7983
type: 'docsVersionDropdown',
8084
position: 'right',
@@ -103,14 +107,15 @@ const config: Config = {
103107
prism: {
104108
theme: prismThemes.github,
105109
darkTheme: prismThemes.oneDark,
110+
additionalLanguages: ['diff', 'bash'],
106111
},
107112
} satisfies Preset.ThemeConfig,
108113

109114
plugins: [
110115
[
111116
'docusaurus-lunr-search',
112117
{
113-
excludeRoutes: ['/docs/v1/**'],
118+
excludeRoutes: ['/docs/v1/**', '/docs/v2/**'],
114119
disableVersioning: true,
115120
},
116121
],

src/components/HomeHeader/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default function HomepageHeader(): React.ReactElement {
66
const { siteConfig } = useDocusaurusContext()
77

88
return (
9-
<header className="hero">
9+
<header className="home-section hero">
1010
<div className="container">
1111
<img
1212
className="hero__logo"
@@ -17,21 +17,22 @@ export default function HomepageHeader(): React.ReactElement {
1717
<p className="hero__subtitle">{siteConfig.tagline}</p>
1818

1919
<p className="hero__description">
20-
Dynamic Domain-Specific Language for Go.<br />
20+
Dynamic Domain-Specific Language for Go.
21+
<br />
2122
Ideal for embedding dynamic content with Go applications
2223
</p>
2324

2425
<div className="hero__buttons">
2526
<Link
2627
className="button button--primary button--lg"
27-
to="/docs/v2/get-started"
28+
to="/docs/v3/get-started"
2829
>
2930
Get started
3031
</Link>
3132

3233
<Link
3334
className="button button--secondary button--lg"
34-
to="/docs/v2/introduction/"
35+
to="/docs/v3/introduction/"
3536
>
3637
Introduction
3738
</Link>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react'
2+
import CodeBlock from '@theme/CodeBlock'
3+
import Tabs from '@theme/Tabs'
4+
import TabItem from '@theme/TabItem'
5+
import { homeViewTw } from '@site/src/modules/textwireCodeExamples/homeViewTw'
6+
import { bookComponentTw } from '@site/src/modules/textwireCodeExamples/bookComponentTw'
7+
import { layoutBaseTw } from '@site/src/modules/textwireCodeExamples/layoutBaseTw'
8+
import { dirStructure } from '@site/src/modules/textwireCodeExamples/dirStructure'
9+
import { goCode } from '@site/src/modules/textwireCodeExamples/goCode'
10+
11+
export default function HomepageFeatures(): React.ReactElement {
12+
return (
13+
<section className="home-section introduction">
14+
<div className="container">
15+
<Tabs>
16+
<TabItem value="home" label="Home page" default>
17+
<CodeBlock language="textwire" showLineNumbers={true} title="templates/home.tw">
18+
{homeViewTw}
19+
</CodeBlock>
20+
</TabItem>
21+
<TabItem value="book" label="Component">
22+
<CodeBlock language="textwire" showLineNumbers={true} title="templates/components/book.tw">
23+
{bookComponentTw}
24+
</CodeBlock>
25+
</TabItem>
26+
<TabItem value="base" label="Layout">
27+
<CodeBlock language="textwire" showLineNumbers={true} title="templates/layouts/base.tw">
28+
{layoutBaseTw}
29+
</CodeBlock>
30+
</TabItem>
31+
<TabItem value="structure" label="Structure">
32+
<CodeBlock>{dirStructure}</CodeBlock>
33+
</TabItem>
34+
<TabItem value="gocode" label="Go Code">
35+
<CodeBlock language="go" showLineNumbers={true} title="cmd/books/main.go">
36+
{goCode}
37+
</CodeBlock>
38+
</TabItem>
39+
</Tabs>
40+
</div>
41+
</section>
42+
)
43+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,4 @@
4343
width: 100%;
4444
border-radius: 50%;
4545
box-shadow: 0 0 10px rgba(0, 0, 0, .2);
46-
}
46+
}

src/css/custom.css

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
@import "_variables";
2-
@import "home/_hero";
3-
@import "home/_features";
4-
@import "community/_page";
2+
@import "home/_index";
3+
@import "community/_index";
4+
5+
button.clean-btn[aria-label="Toggle word wrap"] {
6+
display: none;
7+
}

src/css/home/_features.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
.features {
22
display: flex;
33
align-items: center;
4-
padding: 2rem 0;
54
width: 100%;
65
background-color: var(--bg-secondary);
76
}
@@ -13,4 +12,4 @@
1312
.feature__svg {
1413
height: 200px;
1514
width: 200px;
16-
}
15+
}

0 commit comments

Comments
 (0)