You can format VCL to get unified styled VCL.
falco fmt -h
=========================================================
____ __
/ __/______ / /_____ ____
/ /_ / __ // // __// __ \
/ __// /_/ // // /__ / /_/ /
/_/ \____//_/ \___/ \____/ Fastly VCL developer tool
=========================================================
Usage:
falco fmt [flags] ...files
Flags:
-h, --help : Show this help
-w, --write : Overwrite format result
files argument accepts glob file patterns
Simple format example:
falco fmt /path/to/vcl/main.vcl
Simply you can run formatter as following:
falco fmt /path/to/your/default.vclAnd fmt command accepts multiple source files including glob patterns like:
falco fmt /path/to/your/*.vcl /path/to/another/**/*.vclFormatting rules have default parameter which we recommend but you can override them with format section in configuration file.
See configuration documentation in detail.
Supporting rules are described the following table and sections.
| Name (configuration field) | Type | Default | Description |
|---|---|---|---|
| indent_width | INT | 2 | Specify indent width |
| indent_style | STRING | space | Specify indent style character. Either space(whitespace) or tab(\t) is accepted |
| trailing_comment_width | INT | 2 | Specify space size for trailing comment |
| line_width | INT | 120 | Specify max characters for each line. The overflowed characters are displayed at the next line |
| explicit_string_concat | BOOL | true | Explicitly write string concatenation operator + between expressions |
| sort_declaration_property | BOOL | false | If true, sort declaration properties like table, backend and director alphabetically |
| align_declaration_property | BOOL | false | If true, align declaration properties like table, backend and director |
| else_if | BOOL | false | Coerce use else if keyword for another if statement |
| always_next_line_else_if | BOOL | false | If true, always else if and else keywords print to the next line |
| return_statement_parenthesis | BOOL | true | Coerce surrounded return statement ident by parenthesis |
| sort_declaration | BOOL | false | Sort root declaration by specific order |
| align_trailing_comment | BOOL | false | Align trailing comments |
| comment_style | STRING | none | Coerce comment character. Either sharp(#) or slash(/) is accepted |
| should_use_unset | BOOL | false | Replace remove statement into unset statement |
| indent_case_labels | BOOL | false | If true, add indent for each case statement in switch |
| break_compound_conditions | BOOL | true | If true, split compound if/else if conditions into multiple indented lines |
default: 2
Define the indent width by unit. the formatted statements/propertied will have indent by provided width with Indent Style character.
Before:
sub vcl_recv {
set req.http.Foo = "bar";
}Formatted (indent_width: 2):
sub vcl_recv {
set req.http.Foo = "bar";
}default: space
Define the indent style. space value will use whitespace " " character, or tab value will use tab \t character.
And this character is repeated by Indent Width times.
sub vcl_recv {
set req.http.Foo = "bar";
}Formatted (indent_style: space):
sub vcl_recv {
set req.http.Foo = "bar";
}default: 2
Define trailing comment space size.
sub vcl_recv {
set req.http.Foo = "bar";
}// trailing commentFormatted (trailing_comment_width: 2):
sub vcl_recv {
set req.http.Foo = "bar";
} // trailing commentdefault: 120
Specify max characters for each line. The overflowed characters are printed at the next line with the same ident.
Note
Inserting line-feed is judged for each expression. It means formatter does not split in the middle of a sentence.
You can prevent this formatting rule by providing -1 value to this configuration.
sub vcl_recv {
set req.http.Foo = "lorem" req.http.Sep "ipsum" req.http.Sep "dolor" req.http.Sep "sit" req.http.Sep "amet,";
}Formatted (line_width: 80):
sub vcl_recv {
set req.http.Foo = "lorem" req.http.Sep "ipsum" req.http.Sep "dolor"
req.http.Sep "sit" req.http.Sep "amet,";
}If condition example:
sub vcl_recv {
if (req.http.Header1 == "1" && req.http.Header2 = "2" && req.http.Header3 == "3" && req.http.Header4 == "4") {
req.http.OK = "1";
}
}Formatted (line_width: 80):
sub vcl_recv {
if (
req.http.Header1 == "1" && req.http.Header2 == "2" &&
req.http.Header3 == "3" && req.http.Header4 == "4"
) {
set req.http.OK = "1";
}
}default: true
Print string concatenation character of + explicitly if true.
sub vcl_recv {
set req.http.Foo = "lorem" "ipsum" "dolor" "sit" "amet,";
}Formatted (explicit_string_concat: true):
sub vcl_recv {
set req.http.Foo = "lorem" + "ipsum" + "dolor" + "sit" + "amet,";
}default: false
If true, declaration properties will be printed with alphabetically sorted.
This rule affects to backend, director, and table declarations.
backend example {
.connect_timeout = 1s;
.dynamic = true;
.port = "443";
.host = "example.com";
.first_byte_timeout = 30s;
.max_connections = 500;
.between_bytes_timeout = 30s;
.ssl = true;
.probe = {
.request = "GET / HTTP/1.1" "Host: example.com" "Connection: close";
.dummy = true;
}
}Formatted (sort_declaration_property: true):
backend example {
.between_bytes_timeout = 30s;
.connect_timeout = 1s;
.dynamic = true;
.first_byte_timeout = 30s;
.host = "example.com";
.max_connections = 500;
.port = "443";
.ssl = true;
.probe = {
.dummy = true;
.request = "GET / HTTP/1.1" "Host: example.com" "Connection: close";
}
}default: false
If true, declaration properties will be aligned for max field character length.
This rule affects to backend, director, and table declarations.
backend example {
.connect_timeout = 1s;
.dynamic = true;
.port = "443";
.host = "example.com";
.first_byte_timeout = 30s;
.max_connections = 500;
.between_bytes_timeout = 30s;
.ssl = true;
.probe = {
.request = "GET / HTTP/1.1" "Host: example.com" "Connection: close";
.dummy = true;
}
}`,Formatted (align_declaration_property: true):
backend example {
.connect_timeout = 1s;
.dynamic = true;
.port = "443";
.host = "example.com";
.first_byte_timeout = 30s;
.max_connections = 500;
.between_bytes_timeout = 30s;
.ssl = true;
.probe = {
.request = "GET / HTTP/1.1" "Host: example.com" "Connection: close";
.dummy = true;
}
}default: false
VCL accepts a little kind of another if statement keyword like else if, elseif, and elsif, sometimes it's annoying to recognize.
If this rule set true, print these keywords as else if.
sub vcl_recv {
if (req.http.Foo) {
req.http.Status = "1";
} elseif (req.http.Bar) {
req.http.Status = "2";
} elsif (req.http.Baz) {
req.http.Status = "3";
}
}Formatted (else_if: true):
sub vcl_recv {
if (req.http.Foo) {
req.http.Status = "1";
} else if (req.http.Bar) {
req.http.Status = "2";
} else if (req.http.Baz) {
req.http.Status = "3";
}
}default: true
If true, coerce the surrounding parenthesis state ident of return statement.
sub vcl_recv {
return lookup;
}Formatted (return_statement_parenthesis: true):
sub vcl_recv {
return (lookup);
}default: false
If true, sort root declarations by VCL directive order:
- import/include statements
- ACL
- Backend
- Director
- Table
- vcl_recv
- vcl_hash
- vcl_hit
- vcl_miss
- vcl_pass
- vcl_fetch
- vcl_error
- vcl_deliver
- vcl_log
- user-defined subroutines
sub vcl_fetch {
...
}
sub vcl_deliver {
...
}
sub vcl_recv {
...
}Formatted (sort_declaration: true):
sub vcl_recv {
...
}
sub vcl_fetch {
...
}
sub vcl_deliver {
...
}default: false
If true, align the trailing comment for each statement/declaration line:
sub vcl_recv {
set req.http.X-Forwarded-Host = "example.com"; // request comes from example.com
set req.backend = example_com; // set backend
}Formatted (align_trailing_comment: true):
sub vcl_recv {
set req.http.X-Forwarded-Host = "example.com"; // request comes from example.com
set req.backend = example_com; // set backend
}default: false
If true, the else if and else statement prints to the next line.
sub vcl_recv {
if (req.http.Foo) {
...
} else if (req.http.Bar) {
...
} else {
...
}
}Formatted (always_next_line_else_if: true):
sub vcl_recv {
if (req.http.Foo) {
...
}
else if (req.http.Bar) {
...
}
else {
...
}
}default: true
If true, compound conditions in if and else if are split into multiple lines.
Grouped sub-conditions that include &&/|| are formatted with their own
indented multi-line block.
sub vcl_recv {
if ((beresp.status == 500 || beresp.status == 503) && req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
...
}
}Formatted (break_compound_conditions: true):
sub vcl_recv {
if (
(
beresp.status == 500 ||
beresp.status == 503
) &&
req.restarts < 1 &&
(
req.request == "GET" ||
req.request == "HEAD"
)
) {
...
}
}default: none
Define the comment style. sharp value will use # character, or slash value will use / character.
Note that the inline style comment /* ... */ does not replace.
// Some leading comment
sub vcl_recv {
}Formatted (comment_style: sharp):
## Some leading comment
sub vcl_recv {
}default: false
The remove statement is alias of unset statement, it can be replaced if this rule is true.
sub vcl_recv {
remove req.http.Foo;
}Formatted (should_use_unset: true):
sub vcl_recv {
unset req.http.Foo;
}By default format the placement of case labels at the same indentation level as the outer block. This keeps code in the case blocks indented the same amount as they would be in an if-else-if chain.
default: false
switch (req.http.foo) {
case "bar":
break;
case "baz":
break;
default:
break;
}Formatted (false):
switch (req.http.foo) {
case "bar":
break;
case "baz":
break;
default:
break;
}Formatted (true):
switch (req.http.foo) {
case "bar":
break;
case "baz":
break;
default:
break;
}