三年后,我重写了 Nginx Formatter

💡 原文中文,约8600字,阅读约需21分钟。
📝

内容提要

该文章介绍了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`。

🏷️

标签

➡️

继续阅读