@upstatement/twix 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -10
- package/bin/twix.js +60 -0
- package/package.json +19 -11
- package/bin/twix +0 -23
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Twix
|
|
1
|
+
# Twix 🌱🍫
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An opinionated formatter for [Twig](https://twig.symfony.com/) templating engine. **Entirely coded with Claude.**
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,28 +8,165 @@ A formatter for Twig templates with HTML.
|
|
|
8
8
|
npm install --save-dev @upstatement/twix
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Or download the binary directly from [GitHub Releases](https://github.com/Upstatement/twix/releases).
|
|
12
|
+
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
16
|
# Format a file (in-place)
|
|
15
|
-
|
|
17
|
+
twix path/to/file.twig
|
|
16
18
|
|
|
17
19
|
# Format multiple files
|
|
18
|
-
|
|
20
|
+
twix "src/**/*.twig"
|
|
19
21
|
|
|
20
22
|
# Check if files are formatted (exit 1 if not)
|
|
21
|
-
|
|
23
|
+
twix --check path/to/file.twig
|
|
22
24
|
|
|
23
25
|
# Format from stdin
|
|
24
|
-
cat file.twig |
|
|
26
|
+
cat file.twig | twix --stdin
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
Create a `.twixrc.json` or `twix.config.json` file in your project root:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"indentSize": 2,
|
|
36
|
+
"indentStyle": "space",
|
|
37
|
+
"printWidth": 80,
|
|
38
|
+
"customTags": {
|
|
39
|
+
"block": ["if"],
|
|
40
|
+
"inline": ["set"],
|
|
41
|
+
"intermediate": ["else"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
25
44
|
```
|
|
26
45
|
|
|
46
|
+
### Options
|
|
47
|
+
|
|
48
|
+
| Option | Type | Default | Description |
|
|
49
|
+
| ------------------------- | -------- | --------- | ------------------------------------------------------------------------- |
|
|
50
|
+
| `indentSize` | number | `2` | Number of spaces or tabs per indentation level |
|
|
51
|
+
| `indentStyle` | string | `"space"` | Use `"space"` or `"tab"` for indentation |
|
|
52
|
+
| `printWidth` | number | `80` | Maximum line width before wrapping attributes |
|
|
53
|
+
| `customTags.block` | string[] | `[]` | Custom Twig block tags (tags with bodies, e.g., `{% if %}...{% endif %}`) |
|
|
54
|
+
| `customTags.inline` | string[] | `[]` | Custom Twig inline tags (self-closing, e.g., `{% set = ... %}`) |
|
|
55
|
+
| `customTags.intermediate` | string[] | `[]` | Custom intermediate tags (like `{% else %}` or `{% case %}`) |
|
|
56
|
+
|
|
57
|
+
### Built-in Tag Support
|
|
58
|
+
|
|
59
|
+
**Block tags** (have a body and `{% end* %}` tag):
|
|
60
|
+
|
|
61
|
+
- Standard Twig: `if`, `for`, `block`, `set`, `apply`, `autoescape`, `embed`, `filter`, `macro`, `sandbox`, `verbatim`, `with`
|
|
62
|
+
- Craft CMS: `switch`, `cache`, `js`, `css`, `nav`, `tag`
|
|
63
|
+
|
|
64
|
+
**Inline tags** (self-closing):
|
|
65
|
+
|
|
66
|
+
- Standard Twig: `extends`, `include`, `use`, `import`, `from`, `do`, `flush`, `deprecated`
|
|
67
|
+
- Craft CMS: `exit`, `header`, `paginate`, `redirect`, `requireLogin`, `requireAdmin`, `requireGuest`, `requirePermission`
|
|
68
|
+
|
|
69
|
+
**Intermediate tags** (appear within blocks):
|
|
70
|
+
|
|
71
|
+
- `else`, `elseif`, `case`, `default`
|
|
72
|
+
|
|
27
73
|
## Features
|
|
28
74
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
75
|
+
### HTML Formatting
|
|
76
|
+
|
|
77
|
+
- Proper indentation of nested elements
|
|
78
|
+
- Block elements (`div`, `p`, `section`, etc.) get their own lines
|
|
79
|
+
- Inline elements (`span`, `a`, `strong`, etc.) stay on the same line when possible
|
|
80
|
+
- Void elements (`meta`, `link`, `input`, etc.) are self-closing
|
|
81
|
+
- Multi-line attribute formatting when attributes exceed `printWidth`
|
|
82
|
+
|
|
83
|
+
### Twig Formatting
|
|
84
|
+
|
|
85
|
+
- Block tags are indented with their content
|
|
86
|
+
- Whitespace control modifiers (`{%- -%}`, `{{- -}}`) are preserved
|
|
87
|
+
- Comments (`{# #}`) are preserved exactly as-is (content inside comments is not formatted)
|
|
88
|
+
|
|
89
|
+
### Expression Formatting
|
|
90
|
+
|
|
91
|
+
Twix parses and formats Twig expressions inside `{{ }}` and `{% %}` tags:
|
|
92
|
+
|
|
93
|
+
- Spaces around binary operators (`a + b`), after commas, inside hashes (`{ key: value }`)
|
|
94
|
+
- No spaces around pipes (`value|filter|another`)
|
|
95
|
+
- Strings normalized to double quotes (except when containing unescaped double quotes)
|
|
96
|
+
- Long method chains break with `.` at the start of each line
|
|
97
|
+
- Long function/method calls break arguments onto separate lines with trailing commas
|
|
98
|
+
- Hashes and arrays break onto multiple lines when they exceed `printWidth`
|
|
99
|
+
- Expressions inside string interpolation (`#{...}`) are formatted
|
|
100
|
+
|
|
101
|
+
### Twig Inside HTML Attributes
|
|
102
|
+
|
|
103
|
+
Twix properly handles Twig embedded in HTML attribute values:
|
|
104
|
+
|
|
105
|
+
```twig
|
|
106
|
+
<!-- Expressions in attributes -->
|
|
107
|
+
<div class="container {{ dynamicClass }}">
|
|
108
|
+
|
|
109
|
+
<!-- Conditional classes -->
|
|
110
|
+
<div class="item {% if active %}is-active{% endif %}">
|
|
111
|
+
|
|
112
|
+
<!-- Conditional attributes -->
|
|
113
|
+
<button {% if disabled %}disabled{% endif %}>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Ignore Blocks
|
|
117
|
+
|
|
118
|
+
Skip formatting for specific sections using ignore directives:
|
|
119
|
+
|
|
120
|
+
```twig
|
|
121
|
+
{# twix:ignore-start #}
|
|
122
|
+
<div class="preserve" data-value="exactly as-is" >
|
|
123
|
+
This content will not be formatted
|
|
124
|
+
</div>
|
|
125
|
+
{# twix:ignore-end #}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Blank Line Handling
|
|
129
|
+
|
|
130
|
+
- Multiple consecutive blank lines are collapsed to a single blank line
|
|
131
|
+
- Blank lines immediately after opening block tags (`{% if %}`, `{% for %}`, etc.) are removed
|
|
132
|
+
- Intentional blank lines between statements are preserved
|
|
133
|
+
|
|
134
|
+
## Limitations & Known Issues
|
|
135
|
+
|
|
136
|
+
### Not Supported
|
|
137
|
+
|
|
138
|
+
- **No HTML entity handling** - Entities like ` ` are passed through unchanged
|
|
139
|
+
- **No CSS/JS formatting** - Content inside `<style>` and `<script>` tags is not formatted
|
|
140
|
+
- **Windows line endings** - Output uses Unix line endings (`\n`)
|
|
141
|
+
- **Complex Twig edge cases** - Some advanced Twig syntax may fall back to preserving original formatting
|
|
142
|
+
|
|
143
|
+
## Editor Integration
|
|
144
|
+
|
|
145
|
+
### Pre-commit Hook
|
|
146
|
+
|
|
147
|
+
Using [husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged):
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
// package.json
|
|
151
|
+
{
|
|
152
|
+
"lint-staged": {
|
|
153
|
+
"*.twig": "twix"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Build
|
|
162
|
+
go build
|
|
163
|
+
|
|
164
|
+
# Run tests
|
|
165
|
+
go test ./...
|
|
166
|
+
|
|
167
|
+
# Build for all platforms
|
|
168
|
+
./scripts/build.sh 0.1.0
|
|
169
|
+
```
|
|
33
170
|
|
|
34
171
|
## License
|
|
35
172
|
|
package/bin/twix.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execFileSync } = require("child_process");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
const platform = os.platform();
|
|
8
|
+
const arch = os.arch();
|
|
9
|
+
|
|
10
|
+
const PLATFORMS = {
|
|
11
|
+
"darwin-arm64": "@upstatement/twix-darwin-arm64",
|
|
12
|
+
"darwin-x64": "@upstatement/twix-darwin-x64",
|
|
13
|
+
"linux-arm64": "@upstatement/twix-linux-arm64",
|
|
14
|
+
"linux-x64": "@upstatement/twix-linux-x64",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const platformKey = `${platform}-${arch}`;
|
|
18
|
+
const packageName = PLATFORMS[platformKey];
|
|
19
|
+
|
|
20
|
+
if (!packageName) {
|
|
21
|
+
console.error(`Unsupported platform: ${platform}-${arch}`);
|
|
22
|
+
console.error(
|
|
23
|
+
`twix currently supports: darwin-arm64, darwin-x64, linux-arm64, linux-x64`,
|
|
24
|
+
);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let binaryPath;
|
|
29
|
+
try {
|
|
30
|
+
// Use require.resolve to find the binary in node_modules
|
|
31
|
+
// This works correctly with npm, pnpm, yarn, and monorepos
|
|
32
|
+
const packagePath = require.resolve(`${packageName}/package.json`);
|
|
33
|
+
binaryPath = path.join(path.dirname(packagePath), "bin", "twix");
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error(
|
|
36
|
+
`Could not find the twix binary for your platform (${platformKey}).`,
|
|
37
|
+
);
|
|
38
|
+
console.error(`The package ${packageName} does not appear to be installed.`);
|
|
39
|
+
console.error("");
|
|
40
|
+
console.error("This can happen if:");
|
|
41
|
+
console.error(" 1. Your platform is not supported");
|
|
42
|
+
console.error(
|
|
43
|
+
" 2. Optional dependencies were not installed (try: npm install --include=optional)",
|
|
44
|
+
);
|
|
45
|
+
console.error(
|
|
46
|
+
" 3. You're using a package manager that doesn't install optional dependencies by default",
|
|
47
|
+
);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (e.status !== undefined) {
|
|
55
|
+
process.exit(e.status);
|
|
56
|
+
}
|
|
57
|
+
// If we get here, it's an actual error (not just a non-zero exit code)
|
|
58
|
+
console.error(`Failed to execute twix: ${e.message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstatement/twix",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "An opinionated formatter for Twig templating engine",
|
|
5
|
+
"bin": {
|
|
6
|
+
"twix": "bin/twix.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"optionalDependencies": {
|
|
13
|
+
"@upstatement/twix-darwin-arm64": "0.1.5",
|
|
14
|
+
"@upstatement/twix-darwin-x64": "0.1.5",
|
|
15
|
+
"@upstatement/twix-linux-arm64": "0.1.5",
|
|
16
|
+
"@upstatement/twix-linux-x64": "0.1.5"
|
|
17
|
+
},
|
|
5
18
|
"repository": {
|
|
6
19
|
"type": "git",
|
|
7
20
|
"url": "git+https://github.com/Upstatement/twix.git"
|
|
8
21
|
},
|
|
9
|
-
"optionalDependencies": {
|
|
10
|
-
"@upstatement/twix-darwin-arm64": "0.1.3",
|
|
11
|
-
"@upstatement/twix-darwin-x64": "0.1.3",
|
|
12
|
-
"@upstatement/twix-linux-arm64": "0.1.3",
|
|
13
|
-
"@upstatement/twix-linux-x64": "0.1.3"
|
|
14
|
-
},
|
|
15
22
|
"keywords": [
|
|
16
23
|
"twig",
|
|
17
24
|
"formatter",
|
|
@@ -21,7 +28,8 @@
|
|
|
21
28
|
],
|
|
22
29
|
"author": "Upstatement",
|
|
23
30
|
"license": "MIT",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
}
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/Upstatement/twix/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/Upstatement/twix#readme"
|
|
27
35
|
}
|
package/bin/twix
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Resolve the real path of this script (following symlinks)
|
|
3
|
-
# This is needed because npm creates a symlink in node_modules/.bin/
|
|
4
|
-
SCRIPT="$0"
|
|
5
|
-
while [ -h "$SCRIPT" ]; do
|
|
6
|
-
SCRIPTDIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
|
|
7
|
-
SCRIPT="$(readlink "$SCRIPT")"
|
|
8
|
-
case "$SCRIPT" in
|
|
9
|
-
/*) ;;
|
|
10
|
-
*) SCRIPT="$SCRIPTDIR/$SCRIPT" ;;
|
|
11
|
-
esac
|
|
12
|
-
done
|
|
13
|
-
SCRIPTDIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
|
|
14
|
-
|
|
15
|
-
# SCRIPTDIR is now the real bin/ directory inside @upstatement/twix
|
|
16
|
-
# Go up two levels to @upstatement/, then into the platform package
|
|
17
|
-
case "$(uname -s)-$(uname -m)" in
|
|
18
|
-
Darwin-arm64) exec "$SCRIPTDIR/../../twix-darwin-arm64/bin/twix" "$@" ;;
|
|
19
|
-
Darwin-x86_64) exec "$SCRIPTDIR/../../twix-darwin-x64/bin/twix" "$@" ;;
|
|
20
|
-
Linux-aarch64) exec "$SCRIPTDIR/../../twix-linux-arm64/bin/twix" "$@" ;;
|
|
21
|
-
Linux-x86_64) exec "$SCRIPTDIR/../../twix-linux-x64/bin/twix" "$@" ;;
|
|
22
|
-
*) echo "Unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;;
|
|
23
|
-
esac
|