mcpField
S
sasjs-lint

sasjs-lint

github.com/sasjs测试与质量3657 次安装2 天前更新
代码仓库

关于 sasjs-lint

sasjs-lint 是介绍使用 @sasjs/lint 检查和格式化 SAS 代码的技能参考。它说明配置、规则、格式化器行为,以及 CLI、VS Code 扩展、Server Studio 和库 API 中的用法。

主要功能

  1. 01SAS 源文件检查规则
  2. 02项目级和文件级配置
  3. 03严重级别设置与诊断行为
  4. 04支持的自动修复格式化器
  5. 05通过 CLI、VS Code、Server Studio 或 API 使用

适用场景

  1. 01为 SAS 项目配置 .sasjslint
  2. 02理解或调整 lint 警告
  3. 03将代码检查或格式化加入编辑器工作流或 CI

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sasjs/skills --skill sasjs-lint

安装后在 Claude Code 中输入/sasjs-lint确认已加载。

详细介绍

@sasjs/lint

@sasjs/lint 是 SAS 代码的 lint 和格式化引擎。它是一个不依赖 SAS 的 TypeScript 库——会将 .sas 文件作为文本读取——也是 sasjs lint、SASjs VS Code 扩展的 Problems 面板以及 SASjs Server Studio 编辑器背后的唯一引擎。

本技能介绍规则、.sasjslint 文件、格式化程序及其调用方式。不涉及 SAS 语言本身(参见 sas)或 CLI 的其他部分(参见 sasjs-cli)。

以下规则和默认值已根据 @sasjs/lint 4.5.0 进行核对,该版本由 @sasjs/cli 6.2.5 捆绑。该库会不断增加规则,因此固定使用旧版本的界面可能不包含所有规则——参见 运行位置。

适用场景

  • 编写或修改 .sasjslint 文件
  • sasjs lint 运行报告警告,需要了解其含义或如何屏蔽
  • 将 lint 添加到 CI 流水线或 git 提交前钩子
  • 运行格式化程序(sasjs lint fix、保存时格式化、formatText)
  • 说明编辑器中的某条规则为何触发或未触发

运行位置

界面调用方式备注
SASjs CLIsasjs lint、sasjs lint find、sasjs lint fix、sasjs lint initfind 是默认子命令。init 会在项目根目录写入一个包含完整默认配置的 .sasjslint
VS Code 扩展(sasjs/sasjs-for-vscode,发布于 Open VSX)Problems 面板(ctrl+shift+M)、Format Document、保存时格式化配置优先级:项目 .sasjslint > 设置 sasjs-for-vscode.lintConfig > ~/.sasjslint > 默认值
SASjs Server StudioPOST /SASjsApi/code/lint 和 POST /SASjsApi/code/format,请求体 { code, filePath }规则取自 SASjs Drive 上文件所在文件夹或其上级目录中最近的 .sasjslint,逐级向上查找到 Drive 根目录。首次启动时会在 Drive 根目录预置 .sasjslint,并在文件树中显示为 /.sasjslint
库lintText、lintFile、formatText、formatFile 以及对应的文件夹/项目版本参见下方的 API 部分

这四种界面都调用相同的规则。CLI、扩展和服务各自固定使用一个版本的 @sasjs/lint,因此,只有在某个界面升级其依赖项后,新加入库中的规则才会出现在该界面中——告知用户规则应在编辑器中触发前,请先检查固定使用的版本。

配置解析

未传入配置时,库会按以下顺序解析配置:

  1. 项目根目录中的 .sasjslint(库会逐级向上查找项目)
  2. 用户主目录中的 .sasjslint
  3. DefaultLintConfiguration——内置默认值

未指定的设置会采用默认值,完全没有 .sasjslint 文件的项目也一样。DefaultLintConfiguration 从 @sasjs/lint/utils/getLintConfig 导出,也是 sasjs lint init 写入的内容。

.sasjslint 文件

{
  "noTrailingSpaces": true,
  "maxLineLength": 100,
  "noSingleAsteriskComments": true,
  "severityLevel": {
    "hasDoxygenHeader": "warn",
    "noEncodedPasswords": "error"
  }
}

规则和默认值

分为两组,关闭规则的方式取决于它所属的组。

默认启用 — 使用 false 关闭(另有说明的除外):

设置默认值严重级别检查内容
hasDoxygenHeadertrueWARNING文件是否以 /** Doxygen 头注释开头
hasMacroNameInMendtrueWARNING%mend 是否包含宏名称
hasMacroParenthesestrueWARNING宏定义是否带括号,且 ( 前没有空格
noNestedMacrostrueWARNING是否没有在另一个宏内部定义 %macro
strictMacroDefinitiontrueWARNING参数名称中是否没有空格(%macro m(my var);),是否没有无法识别的选项(%macro m()/nonsense;)
noUndeclaredMacrostrueWARNING文件调用的每个宏是否都在文件中或其头注释中声明
noUnusedMacrostrueWARNING<h4> SAS Macros </h4> 中的每个宏是否都实际被调用
noNestedCommentsInHeadertrueWARNING开头的 Doxygen 头注释中是否没有 */。SAS 会将其视为注释结束标记;其后的所有内容,包括 @param 行和用法示例,都会作为 SAS 代码编译
noBannedStringstrueWARNING每行是否都不包含配置的字符串。默认检查类似日志内容的词语 error 、error:、warning 、warning:。不区分大小写,注释也会检查
noTrailingSpacestrueWARNING行尾是否没有空白字符
noTabstrueWARNING是否没有制表符。别名:noTabIndentation(已弃用)
noGremlinstrueWARNING是否没有零宽字符或其他非标准字符
indentationMultiple2WARNING行首空格数是否能被该数字整除
lowerCaseFileNamestrueWARNING文件名是否为小写(*nix 上的自动调用要求文件名为小写)
noSpacesInFileNamestrueWARNING文件名中是否没有空格
noEncodedPasswordstrueERROR是否没有 {sas00X} 或 {sasenc} 编码密码
maxLineLength80WARNING行长度。设为 0 可关闭

默认关闭 — 使用 true 启用:

设置默认值严重级别检查内容
noSingleAsteriskCommentsfalseWARNING是否没有 * text; 形式的注释语句
noUnusedLibnamesfalseWARNING文件是否没有分配后从未使用的 libref
hasRequiredMacroOptionsfalseWARNING宏是否包含 requiredMacroOptions 中的选项
lineEndings"off"WARNING行尾是否符合 lf 或 crlf

辅助设置(非规则):

设置默认值用途
ignoreList[]要跳过的路径。.gitignore 中的文件也会跳过
allowedGremlins[]4 位十六进制字符串,例如 "0x0080"
defaultHeader见下文应用于没有 /** 头注释的文件的头注释模板
severityLevel{}按规则覆盖 "warn" / "error"
maxHeaderLineLength80头注释中的行长限制
maxDataLineLength80datalines / cards / parmcards 记录中的行长限制
requiredMacroOptions[]hasRequiredMacroOptions 要求的选项
ignoredLibnames[]noUnusedLibnames 不检查的 libref
ignoredMacros["webout"]noUndeclaredMacros 视为已声明的宏名称。设置后会替换默认值 — 见下文
bannedStrings["error ", "error:", "warning ", "warning:"]noBannedStrings 要报告的字符串。设置后会替换默认值 — 见下文

严重级别和退出代码

每条规则有三种状态:关闭、WARNING 或 ERROR。sasjs lint 仅在报告 ERROR 时返回非零退出代码 — 警告不会改变退出代码,因此只产生警告的文件仍会通过基于退出代码判断的钩子或流水线。noEncodedPasswords 是唯一默认严重级别为 ERROR 的规则。

severityLevel 可按规则名称提升或降低严重级别,只接受 "warn" 和 "error";其他值会被忽略,且不会提示:

{
  "severityLevel": {
    "maxLineLength": "error",
    "hasDoxygenHeader": "warn"
  }
}

文件级覆盖

文件通过其 Doxygen 头部中的 @sasjslint 块调整适用于自身的规则:

/**
  @file
  @brief Calls the settlement API

  @sasjslint {"maxHeaderLineLength": 200, "noUndeclaredMacros": false}

  <h4> SAS Macros </h4>
  @li mf_trim.sas

**/
  • 标量会替换原值,数组会追加(allowedGremlins、requiredMacroOptions、ignoredLibnames 会增加条目而非替换),对象会逐键合并(severityLevel)。
  • 覆盖项也适用于格式化程序,因此 sasjs lint fix 会遵循它——关闭 noUnusedMacros 的文件会保留否则会被删除的头部条目。
  • 只读取头部,且只识别紧跟标签并以 { 开头的行。格式错误或未闭合的块会被忽略,不会引发错误。
  • 头部设置优先于调用方传入的配置。
  • ignoreList 是头部不能使用的唯一设置:它会在读取文件前进行评估。
  • 请将该块放在 <h4> SAS Macros </h4> 部分之外,格式化程序会重写该部分。

ignoredMacros 和 bannedStrings

这两项都是列表,其项目级值会替换内置默认值,而不是在默认值上追加:显式设置 ignoredMacros 会移除 webout,除非重新列出;"bannedStrings": [] 则完全不报告任何内容。单文件的 @sasjslint 块恰好相反——其中的数组会追加,因此文件会在不必重述项目列表的情况下添加条目。

ignoredMacros 仅由 noUndeclaredMacros 读取。它对 noUnusedMacros 没有作用,因此无法用这种方式消除相反的警告——在头部声明但从未调用的宏;请移除该条目,或为该文件关闭该规则。

ignoredMacros 的用途:通过提供程序引入的宏

Lint 工具只读取一个文件和配置。它不了解构建的其他部分,而 sasjs lint 不会从 sasjsconfig.json 向它传递任何信息。因此,调用宏的文件必须声明该宏,即使编译过程通过其他方式提供了该宏:

  • serviceInit 程序(目标配置中的 serviceInit,会编译到每个服务中)会在自身的 <h4> SAS Macros </h4> 中声明宏文件——服务主体可以调用这些宏而无需声明,但每次调用都会收到一次警告;
  • sasjs test 中的 testSetUp / testInit 程序,结构相同;
  • 编译器自行注入的内容:内置情形是 %webout,这也是默认列表包含 webout 的原因。

对于这些情况,项目的 .sasjslint 是合适的配置位置:

{
  "ignoredMacros": ["webout", "mpeinit", "mpeterm"]
}

请注意两点。由于列表会替换默认值,请重新列出 webout;否则,每个输出流的服务中都会报告 %webout。此外,项目级条目也会在宏确实缺失的文件中关闭检查,因此只应列出那些能被所有可能调用它的文件获取的宏;如果提供程序只覆盖少数文件,而非全部文件,那么使用文件级 @sasjslint {"ignoredMacros": [...]} 会更精准。

bannedStrings

四个默认值都是看起来像日志消息的词:注释或 %put 中出现这些词时,它们会出现在 SAS 日志的 grep 结果中,也会出现在任何基于此类 grep 的日志扫描器结果中,因此真正的问题很容易淹没在仅仅是文字说明的匹配项里。匹配不区分大小写,按字符串长度从长到短进行,且不会对重叠的范围重复报告;同时会跳过两类内容:

  • datalines / cards / parmcards 记录,其中的文本是数据而非代码;
  • 包含 @sasjslint 块的那一行,因此文件可以在自己的覆盖设置中配置禁用字符串。

请改写说明文字。如果文本必须原样保留在日志中,请用宏函数拆开该词:%put %str(WARN)ING: message 会打印 WARNING: message,且不再包含该字面字符串。Doxygen 的 @warning 标签属于内容而非说明文字,因此此类文件需要设置 @sasjslint {"noBannedStrings": false}。

格式化器

格式化器会在保存时应用可修复的规则。目前已实现:

  • 将宏名称添加到 %mend
  • 当文件中没有 Doxygen 头部时,添加一个模板
  • 将文件使用的宏添加到 <h4> SAS Macros </h4>,去重并排序
  • 从该部分移除文件未使用的宏
  • 移除行尾空格

尚未实现(请勿承诺这些功能):将制表符转换为空格、移除 gremlin 字符、修正换行符、将单星号注释转换为块注释。

defaultHeader 使用 {lineEnding} 作为换行符占位符——字面值 \n 不起作用:

{
  "defaultHeader": "/**{lineEnding}  @file{lineEnding}  @brief Our Company Brief{lineEnding}**/"
}

库 API

import { lintText, lintFile, formatText, formatFile, LintConfig, Severity } from '@sasjs/lint'

const diagnostics = await lintText(code, config)   // config optional
const fixed = await formatText(code, config)       // config optional
  • lintText(text, configuration?)、lintFile(path, configuration?)、lintFolder、lintProject
  • formatText(text, configuration?)、formatFile、formatFolder、formatProject
  • new LintConfig(json) 从普通对象构建配置;config.override(json) 返回合并了覆盖项的新配置
  • Diagnostic 包含 message、lineNumber、startColumnNumber、endColumnNumber 和 severity
  • Severity 为 Info(0)、Warning(1)、Error(2)

当调用方知道规则时,传入显式的 LintConfig 很重要——对没有磁盘路径的缓冲区进行检查的服务或编辑器,无法依赖工作目录遍历。

常见问题

  • 不同规则中,“关闭”的含义不同。 默认开启的规则使用 false 关闭;maxLineLength 使用 0 关闭;lineEndings 使用 "off" 关闭;默认关闭的规则会忽略 false(效果与不设置相同),需要设为 true 才能开启。设置错误是规则“无法生效”最常见的原因。
  • 当 maxLineLength 关闭时,maxHeaderLineLength 和 maxDataLineLength 不起作用,因为它们的作用范围受其约束。有效限制取两个值中较高的那个,因此将其中一个设为低于 maxLineLength 的值会被静默忽略。
  • 无论是 ignoredMacros 还是 bannedStrings,显式列表都会替换默认列表。 只有未设置该项时才会使用默认值,因此 "ignoredMacros": ["mpeinit"] 会让 webout 不再被忽略,而 "bannedStrings": [] 则不会报告任何内容,也不会回退到默认值。请重新列出你仍想保留的默认项。
  • noUnusedMacros 配合格式化程序会移除 @li 依赖项。 这些条目会被编译器使用,因此,如果某个宏仅通过动态 %&name 调用间接引用,或由 %include 引入的程序提供,就会失去其声明,导致作业无法汇编。遇到这种情况时,请在项目或文件级别关闭此规则。
  • 即使文件没有调用某个宏,头部条目也可能是构建所必需的,因此删除前请先验证。 编译器还会内联另一个已声明宏的 <h4> 所要求的内容,因此某个条目可能只是该调用链中的重复项(可安全删除,且生成的程序不会改变),也可能是该宏被引入文件的唯一途径(删除后会导致构建失败)。验证方法是编译并 grep 生成的文件:编辑前后分别运行 sasjs compile,然后运行 grep -c "%macro <name>" sasjsbuild/services/<path>.sas。
  • noUndeclaredMacros 不会识别 SASAUTOS。 SAS 自带的宏始终会被视为已声明(列表由 @sasjs/sas-language 生成),但通过 SASAUTOS 提供的宏对代码检查器不可见,因此会被报告。请在头部声明这些宏,或关闭此规则。
  • noBannedStrings 会报告注释或头部说明中的词语,而不仅仅是代码中的词语。 文件看起来没有问题,仍可能触发此规则;[line, column] 指向该词语本身。bannedStrings 下介绍了三种处理方式:改写、使用 %str(WARN)ING,或针对 doxygen 标签设置单文件覆盖。
  • noUnusedLibnames 的判定范围有意设置得较宽。 只要 libref 出现在 LIBNAME 语句之外的任何位置,就会被视为已使用,包括双引号字符串中的出现,例如 pathname("outData", "L")。由于单个文件并非完整作业,且此规则没有格式化修复,因此默认关闭。
  • 只有对于默认值此前按条件应用的设置,空的 .sasjslint 才与没有 .sasjslint 不同。 从 4.0.0 开始,未设置的 maxLineLength 或 hasMacroNameInMend 会回退到文档规定的默认值(分别为 80 和 true)。因此,若项目之前依靠省略这些设置来关闭相应规则,现在必须设置 "maxLineLength": 0 或 "hasMacroNameInMend": false。
  • allowedGremlins 中的条目是字符串,而不是数字:"0x0080",必须恰好包含四位十六进制数字。
  • CLI 输出为框线表格。 该输出供终端使用;不要将其粘贴到纯文本或仅支持 ASCII 的文档中,否则边框字符可能会造成问题。
  • 不带子命令的 sasjs lint 等同于 sasjs lint find,会检查项目中的所有 .sas 文件。

验证

  • 确认规则是否触发:将违规构造放入 .sas 文件,然后运行 terminal(command="npx sasjs lint")。命中时会显示一行消息以及 [line, column] 位置;没有显示行表示该规则已关闭或该构造被排除(先检查配置,再检查规则的排除项)。
  • 确认配置更改是否生效:编辑 .sasjslint 前后分别运行 npx sasjs lint,并比较显示的行。默认关闭的规则需要设为 true,而不是 false。
  • 确认格式化程序的效果:运行 npx sasjs lint fix 并检查差异——它会改写 <h4> SAS Macros </h4> 部分和 %mend 行。
  • 确认单文件覆盖设置:添加 @sasjslint 块,运行 sasjs lint,并检查被关闭的规则是否不再针对该文件报告,同时仍针对相邻文件报告。
  • 确认禁用字符串:在文件中加入 %put WARNING: test; 并运行 sasjs lint——该行对应的位置会出现一条报告。将其改写为 %put %str(WARN)ING: test; 后,报告会消失,而日志行仍显示 WARNING: test。
  • 确认 ignoredMacros:对调用了未声明宏的文件运行 lint,将该宏的名称加入项目的 .sasjslint,报告便会消失。然后从同一列表中移除 webout,并确认在调用它的服务中 %webout 开始触发报告——该列表会替换默认列表,而不是在其基础上扩展。

评论

支持 Markdown