Skip to content

Commit

Permalink
add golangci linter, improve tests, add ghaction for golangci linter
Browse files Browse the repository at this point in the history
  • Loading branch information
vigo committed Feb 27, 2024
1 parent 0e92f51 commit 7e7d6be
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 48 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/go-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Run Golang CI Lint

on:
pull_request:
paths:
- '**.go'

concurrency:
group: golangci-lint-templ8go
cancel-in-progress: true

jobs:
golangci:
name: golangci linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout=5m
7 changes: 2 additions & 5 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go
name: Run Go build and test

on:
push:
Expand All @@ -19,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
go-version-file: "go.mod"

- name: Build
run: go build -v ./...
Expand Down
139 changes: 139 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
run:
concurrency: 4
timeout: 1m

linters-settings:
tagalign:
align: false
sort: false
govet:
check-shadowing: true
enable:
- asmdecl
- assign
- atomic
- atomicalign
- bools
- buildtag
- cgocall
- composites
- copylocks
- deepequalerrors
- errorsas
- findcall
- framepointer
- httpresponse
- ifaceassert
- loopclosure
- lostcancel
- nilfunc
- nilness
- printf
- reflectvaluecompare
- shadow
- shift
- sigchanyzer
- sortslice
- stdmethods
- stringintconv
- structtag
- testinggoroutine
- tests
- unmarshal
- unreachable
- unsafeptr
- unusedresult
wrapcheck:
ignoreSigs:
- httperror.New(
- .Errorf(
- errors.New(
- errors.Unwrap(
- .Wrap(
- .Wrapf(
- .WithMessage(
- .WithMessagef(
- .WithStack(
- .WrapError(
ignoreSigRegexps:
- \.New.*Error\(
ignorePackageGlobs:
- encoding/*
- github.com/pkg/*
revive:
ignore-generated-header: true
severity: warning
rules:
- name: exported
severity: warning
- name: error-return
severity: warning
- name: error-naming
severity: warning
- name: if-return
severity: warning
- name: var-naming
severity: warning
- name: var-declaration
severity: warning
- name: receiver-naming
severity: warning
- name: errorf
severity: warning
- name: empty-block
severity: warning
- name: unused-parameter
severity: warning
- name: unreachable-code
severity: warning
- name: redefines-builtin-id
severity: warning
- name: superfluous-else
severity: warning
- name: unexported-return
severity: warning
- name: indent-error-flow
severity: warning
- name: blank-imports
severity: warning
- name: range
severity: warning
- name: time-naming
severity: warning
- name: context-as-argument
severity: warning
- name: context-keys-type
severity: warning
- name: indent-error-flow
severity: warning

linters:
disable-all: true
enable:
- asciicheck
- durationcheck
- errcheck
- errorlint
- exhaustive
- gosec
- govet
- makezero
- nilerr
- exportloopref
- staticcheck
- typecheck
- bodyclose
- noctx
- prealloc
- gosimple
- ineffassign
- unparam
- unused
presets:
- comment
- error
- format
- metalinter

issues:
exclude-use-default: false
51 changes: 36 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
# templ8go

`templ8go` is a small library designed to resolve template strings like `Hello {{ user.name }}` utilizing the V8
JavaScript engine. The play on words in the name stems from blending "template" with "V8" (the JavaScript engine),
and "8" phonetically resembling "ate". Sorry for the dad joke, ChatGPT come up with it.
`templ8go` is a small library designed to resolve template strings like `Hello
{{ user.name }}` utilizing the V8 JavaScript engine. The play on words in the
name stems from blending "template" with "V8" (the JavaScript engine), and "8"
phonetically resembling "ate". Sorry for the dad joke, ChatGPT come up with it.

## Why?

While traditional templating engines are great for generating large blocks of text and support a variety of built-in
functions and pipe syntax, `templ8go` takes a different approach. It leverages the power and flexibility of JavaScript
for manipulating and interpolating short strings. This allows for more dynamic and programmable template resolution
without leaving the comfort of Go.
While traditional templating engines are great for generating large blocks of
text and support a variety of built-in functions and pipe syntax, `templ8go`
takes a different approach. It leverages the power and flexibility of
JavaScript for manipulating and interpolating short strings. This allows for
more dynamic and programmable template resolution without leaving the comfort
of Go.

Consider the following examples where `templ8go` shines:

- `Hello {{ user.name }}` with `{user:{name: Mustafa}}` resolves to `Hello Mustafa`.
- `Your balance is {{ account.balance.toFixed(2) }}` with `{account:{balance: 1234.567}}` becomes `Your balance is 1234.57`.

These examples showcase the simplicity and power of using JavaScript expressions within templates.
These examples showcase the simplicity and power of using JavaScript
expressions within templates.

## Features

- **Dynamic Expression Evaluation**: Use JavaScript expressions right within your template strings.
- **Bindings Support**: Seamlessly pass Go variables into the JavaScript execution context to be used within your template expressions.
- **Easy Integration**: Designed to be easily integrated into any Go project that needs flexible string interpolation.
- **Security**: It leverages V8, the same Javascript engine that runs Cloudflare workers and Chrome. Though we can add even more hardening.
- **Dynamic Expression Evaluation**: Use JavaScript expressions right within
your template strings.
- **Bindings Support**: Seamlessly pass Go variables into the JavaScript
execution context to be used within your template expressions.
- **Easy Integration**: Designed to be easily integrated into any Go project
that needs flexible string interpolation.
- **Security**: It leverages V8, the same Javascript engine that runs
Cloudflare workers and Chrome. Though we can add even more hardening.

## Getting Started

### Installation

First, ensure you have Go installed on your machine (version 1.21 or newer is recommended). Then, install `templ8go` using `go get`:
First, ensure you have Go installed on your machine (version 1.21 or newer is
recommended). Then, install `templ8go` using `go get`:

```sh
go get -u github.com/mustafaakin/templ8go
Expand Down Expand Up @@ -93,6 +102,17 @@ func main() {
}
```

You can change the default execution timeout via calling;

```go
// now you have 200 Milliseconds for execution
SetDefaultExecutionTimeout(200 * time.Millisecond)

// rest is the same...
result, err := templ8go.ResolveJSExpression(bindings, "user.name")
...
```

## Supported Expressions

Since we use V8 engine underneath, many things are possible.
Expand Down Expand Up @@ -155,8 +175,9 @@ Feel free to submit pull requests or create issues for bugs, features, or sugges

## Contributing

Contributions are more than welcome! If you have an idea for an improvement or find a bug, please feel free
to fork the repository, make your changes, and submit a pull request.
Contributions are more than welcome! If you have an idea for an improvement or
find a bug, please feel free to fork the repository, make your changes, and
submit a pull request.

## License

Expand Down
32 changes: 22 additions & 10 deletions js.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ package templ8go

import (
"encoding/json"
"errors"
"fmt"
v8 "rogchap.com/v8go"
"time"

v8 "rogchap.com/v8go"
)

var defaultExecutionTimeout = 100 * time.Millisecond

// sentinel errors.
var (
ErrResolveJSExpressionExecutionTimeout = errors.New("execution timeout error")
)

// SetDefaultExecutionTimeout overrides default execution timeout.
func SetDefaultExecutionTimeout(d time.Duration) {
defaultExecutionTimeout = d
}

// ResolveJSExpression handles the resolve operation with a given JS expression and binding data.
func ResolveJSExpression(bindings map[string]interface{}, expression string) (interface{}, error) {
ctx := v8.NewContext()
defer ctx.Close()
Expand All @@ -17,25 +32,22 @@ func ResolveJSExpression(bindings map[string]interface{}, expression string) (in
return nil, fmt.Errorf("failed to marshal value for key %s: %w", key, err)
}

// Directly use the marshaled JSON string instead of converting back to string.
if err := ctx.Global().Set(key, string(j)); err != nil {
return nil, fmt.Errorf("failed to set global property %s: %w", key, err)
}

script := fmt.Sprintf("%s = JSON.parse(%s)", key, key)
if _, err := ctx.RunScript(script, ""); err != nil {
return nil, fmt.Errorf("failed to parse global property %s: %w", key, err)
if _, err := ctx.RunScript(key+" = JSON.parse("+key+")", ""); err != nil {
return nil, fmt.Errorf("failed to run script, key: %s: %w", key, err) // JSError
}
}

resultChan := make(chan interface{}, 1)
errorChan := make(chan error, 1)

go func() {
script := fmt.Sprintf("JSON.stringify(%s)", expression)
val, err := ctx.RunScript(script, "")
val, err := ctx.RunScript("JSON.stringify("+expression+")", "")
if err != nil {
errorChan <- fmt.Errorf("failed to evaluate expression: %w", err)
errorChan <- fmt.Errorf("%w, expression was: %s", err, expression)
return
}

Expand All @@ -53,8 +65,8 @@ func ResolveJSExpression(bindings map[string]interface{}, expression string) (in
return result, nil
case err := <-errorChan:
return nil, err
case <-time.After(100 * time.Millisecond): // TODO: configurable
case <-time.After(defaultExecutionTimeout):
ctx.Isolate().TerminateExecution()
return nil, fmt.Errorf("execution timeout")
return nil, ErrResolveJSExpressionExecutionTimeout
}
}
Loading

0 comments on commit 7e7d6be

Please sign in to comment.