NPM版本范围语法是否非正则?JSON Schema校验咨询
npm版本范围语法的正则性校验与替代方案
一、该语法是否为正则语言?
你的假设不成立:npm semver版本范围语法属于正则语言,完全可以用正则表达式完整描述。
从你提供的BNF规则来看,所有语法单元都由有限状态规则定义(仅包含正则闭包操作*,无嵌套递归逻辑),因此符合正则语言的特征。
对应的正则表达式
以下是匹配该语法的完整正则(基于semver官方实现整理,覆盖所有BNF规则):
^(?:(?:(?:<|>|>=|<=|=|~|\^)?(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?)|(?:(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*-\s*(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?))(?:\s+(?:(?:<|>|>=|<=|=|~|\^)?(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?)|(?:(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*-\s*(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?)))*(?:\s*\|\|\s*(?:(?:(?:<|>|>=|<=|=|~|\^)?(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?)|(?:(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*-\s*(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?))(?:\s+(?:(?:<|>|>=|<=|=|~|\^)?(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?)|(?:(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*-\s*(?:x|X|\*|[0-9]+)(?:\.(?:x|X|\*|[0-9]+))?(?:\.(?:x|X|\*|[0-9]+))?(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?))*)?$
该正则支持所有BNF定义的场景:
||分隔的多范围集合(如1.2.7 || >=1.2.9 <2.0.0)>/>=/</<=/=等比较符前缀x/X/*作为通配符的版本段(如1.x、*)~/^前缀的模糊匹配(如~1.2.3、^2.0.0)- 连字符范围(如
1.2.3 - 1.4.5) - 带预发布标识(
-alpha.1)和构建号(+build.2)的版本
二、替代校验方案(若正则过于复杂)
如果觉得上述正则冗长难维护,或需要更精准的语义校验(排除语法合法但逻辑无效的范围),可以采用以下方法:
1. JSON Schema自定义格式
在JSON Schema中用format关键字定义自定义格式,然后在验证逻辑中调用semver专业库的校验方法:
{ "type": "string", "format": "npm-semver-range" }
验证时通过库方法判断:
const semver = require('semver'); function validateSemverRange(str) { return semver.validRange(str) !== null; }
2. 应用层前置校验
不依赖JSON Schema的pattern或format,在数据进入Schema校验前,先用semver库做语法校验,确保输入符合要求后再执行其他规则校验。
3. 拆分规则的Schema校验
将复杂规则拆分为多个简单正则,通过allOf组合提升可读性:
{ "type": "string", "allOf": [ { "pattern": "^(?:\\s*(?:[<>]=?|=|~|\\^)?[\\w.-]+|\\s*[\\w.-]+\\s*-\\s*[\\w.-]+)(?:\\s+(?:[<>]=?|=|~|\\^)?[\\w.-]+|\\s*[\\w.-]+\\s*-\\s*[\\w.-]+)*\\s*(?:\\|\\|\\s*(?:\\s*(?:[<>]=?|=|~|\\^)?[\\w.-]+|\\s*[\\w.-]+\\s*-\\s*[\\w.-]+)(?:\\s+(?:[<>]=?|=|~|\\^)?[\\w.-]+|\\s*[\\w.-]+\\s*-\\s*[\\w.-]+)*\\s*)*$" }, { "not": { "pattern": "^\\s*$" } } ] }
内容的提问来源于stack exchange,提问作者carl
相关产品推荐
相关产品推荐

