三年后,我重写了 Nginx Formatter
内容提要
该文章介绍了nginx-formatter工具从v1到v2的重构过程。v1基于字符串规则和JavaScript引擎,存在转义字符丢失、引号处理错误等问题。v2改用原生Go实现Lexer、Parser、AST和Printer,支持单文件处理,并通过Cobra拆分命令。新版本修复了语法上下文缺失问题,性能提升约17倍,体积减半,同时增强了测试覆盖和安全性。
延伸解读
从字符串规则到语法树:重构的核心动机
旧版基于字符串规则和JavaScript引擎,缺乏语法上下文,导致转义字符丢失、引号处理错误等问题。新版改用原生Go实现Lexer、Parser、AST和Printer,明确区分字符串、注释、变量等上下文,从根本上修复了这些缺陷。这一重构体现了格式化工具从“表面美化”到“语法正确”的转变。
性能与体积的显著提升
移除Goja后,Linux amd64发布文件缩小近一半,处理1000个配置文件的墙钟时间中位数从2.12秒降至0.12秒,约快17倍。虽然作者指出这不能作为通用基准,但足以说明减少逐文件创建JavaScript虚拟机带来的开销,对批量处理场景有明显改善。
兼容性与使用注意事项
v2.2.0引入Cobra子命令,但保留旧参数以兼容脚本。用户需注意:位置参数无效,必须使用-i;单文件写入非原子,覆盖前应备份;目录模式省略-o时输出到当前工作目录,避免误覆盖。这些细节对实际使用至关重要。
安全性与边界处理
文件访问改用os.OpenRoot和fs.WalkDir,拒绝通过..或符号链接越出目录,修复了相对路径问题。但当前仅对*_by_lua_block做专门处理,其他嵌入语言仍需支持;未闭合引号等错误诊断仍不严格。用户应了解这些限制,避免在复杂配置上依赖工具。
Q&A
nginx-formatter v2相比v1有哪些主要改进?
v2用原生Go的Lexer、Parser、AST和Printer替换了基于字符串规则和JavaScript引擎的旧内核,修复了转义字符丢失、引号处理错误等问题,性能提升约17倍,体积减半,并增强了测试覆盖和安全性。
nginx-formatter v1存在哪些已知问题?
v1存在转义字符丢失、单引号JSON被拆坏、空字符串return被改写、不能处理单文件等问题,根源是字符串规则缺少语法上下文。
nginx-formatter v2.2.0的命令行用法是怎样的?
v2.2.0使用Cobra拆分了format、serve、version三个子命令。例如:格式化目录用`nginx-formatter format -i ./conf.d -o ./dist`,启动WebUI用`nginx-formatter serve -p 8080`,查看版本用`nginx-formatter version`。
nginx-formatter如何处理OpenResty的Lua块?
新Parser将名称以`_by_lua_block`结尾的块识别为RawBlock,不按Nginx指令解析,而是用专门扫描逻辑保存嵌入代码,跳过Lua长字符串和注释中的花括号,只统一公共前导缩进。
nginx-formatter v2的性能提升有多大?
在相同环境下处理1000个配置文件,墙钟时间中位数从2.12秒降到0.12秒,约快17倍。
nginx-formatter v2.2.0如何保证格式化结果的幂等性?
通过解析和输出分离,测试验证了`format(format(input)) == format(input)`的性质,覆盖了转义空格、缺失分号、注释、Lua RawBlock等场景。
nginx-formatter v2.2.0在文件访问安全方面做了哪些改进?
改用`os.OpenRoot`和`fs.WalkDir`,所有目录文件通过根目录内的相对路径访问,修复了相对路径问题,并拒绝通过`..`或符号链接越出指定目录。
nginx-formatter v2.2.0如何安装?
可以通过Homebrew Tap安装:`brew tap soulteary/tap`然后`brew install soulteary/tap/nginx-formatter`,也可以从GitHub发布页面下载对应平台的二进制文件,或使用Docker镜像`soulteary/nginx-formatter:v2.2.0`。