sasjs-lint
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese@sasjs/lint
@sasjs/lint
<!-- SPDX-License-Identifier: MIT -->
is the linting and formatting engine for SAS code. It is a TypeScript library with no SAS dependency - it reads files as text - and it is the single engine behind , the SASjs VS Code extension's Problems panel, and the SASjs Server Studio editor.
@sasjs/lint.sassasjs lintThis skill covers the rules, the file, the formatter, and the ways to invoke it. It does not cover the SAS language itself (see ) or the rest of the CLI (see ).
.sasjslintsassasjs-cli<!-- SPDX-License-Identifier: MIT -->
是 SAS 代码的 Lint 和格式化引擎。它是一个不依赖 SAS 的 TypeScript library——以文本方式读取 文件——并且是 、SASjs VS Code extension 的 Problems panel 以及 SASjs Server Studio editor 背后的唯一引擎。
@sasjs/lint.sassasjs lint本 skill 介绍规则、 文件、formatter 以及调用方式,不涵盖 SAS language 本身(参见 ),也不涵盖 CLI 的其他部分(参见 )。
.sasjslintsassasjs-cliWhen to use
何时使用
- Writing or changing a file
.sasjslint - A run reports a warning and you need to know what it means or how to silence it
sasjs lint - Adding lint to a CI pipeline or a git pre-commit hook
- Running the formatter (, format-on-save,
sasjs lint fix)formatText - Explaining why a rule does or does not fire in an editor
- 编写或修改 文件
.sasjslint - 运行 后出现警告,需要了解其含义或如何忽略该警告
sasjs lint - 将 Lint 添加到 CI pipeline 或 git pre-commit hook
- 运行 formatter(、format-on-save、
sasjs lint fix)formatText - 解释某条规则在 editor 中是否会触发以及为什么
Where it runs
运行位置
| Surface | How it is invoked | Notes |
|---|---|---|
| SASjs CLI | | |
VS Code extension ( | Problems panel (ctrl+shift+M), Format Document, format-on-save | Config precedence: project |
| SASjs Server Studio | | Rules come from the nearest |
| Library | | See the API section below |
All four call the same rules. The CLI, extension and server each pin a version of , so a rule added to the library only appears in a surface once that surface bumps its dependency - check the pinned version before telling a user a rule should fire in their editor.
@sasjs/lint| 使用场景 | 调用方式 | 说明 |
|---|---|---|
| SASjs CLI | | |
VS Code extension( | Problems panel(ctrl+shift+M)、Format Document、format-on-save | 配置优先级:项目 |
| SASjs Server Studio | | 规则来自 SASjs Drive 中文件所在目录或更高层级目录内最近的 |
| Library | | 参见下方 API 部分 |
四种调用方式均使用同一套规则。CLI、extension 和 server 各自固定一个 版本,因此 library 中新增的规则只有在该入口升级依赖后才会出现。在告知用户某条规则应该在其 editor 中触发之前,请先检查固定的版本。
@sasjs/lintConfig resolution
配置解析
When no configuration is passed in, the library resolves it in this order:
- at the project root (the library walks up to find the project)
.sasjslint - in the user's home directory
.sasjslint - - the built-in defaults
DefaultLintConfiguration
A setting that is absent takes its default, and so does a project with no at all. is exported from , and it is what writes out.
.sasjslintDefaultLintConfiguration@sasjs/lint/utils/getLintConfigsasjs lint init未传入 configuration 时,library 按以下顺序解析配置:
- 项目根目录中的 (library 会向上查找以确定项目)
.sasjslint - 用户主目录中的
.sasjslint - ,即内置默认配置
DefaultLintConfiguration
缺失的设置会采用默认值;完全没有 的项目也是如此。 从 导出,同时也是 写入的内容。
.sasjslintDefaultLintConfiguration@sasjs/lint/utils/getLintConfigsasjs lint initThe .sasjslint file
.sasjslint
文件
.sasjslintjson
{
"noTrailingSpaces": true,
"maxLineLength": 100,
"noSingleAsteriskComments": true,
"severityLevel": {
"hasDoxygenHeader": "warn",
"noEncodedPasswords": "error"
}
}json
{
"noTrailingSpaces": true,
"maxLineLength": 100,
"noSingleAsteriskComments": true,
"severityLevel": {
"hasDoxygenHeader": "warn",
"noEncodedPasswords": "error"
}
}Rules and defaults
规则与默认值
Two groups, and the way you switch a rule off depends on which group it is in.
On by default - switch off with (except where noted):
false| Setting | Default | Severity | What it checks |
|---|---|---|---|
| true | WARNING | the file begins with a |
| true | WARNING | |
| true | WARNING | macros are defined with parentheses, and no space before the |
| true | WARNING | no |
| true | WARNING | no space in a parameter name ( |
| true | WARNING | every macro a file calls is declared in the file or its header |
| true | WARNING | every macro in |
| true | WARNING | no line ends in whitespace |
| true | WARNING | no tab characters. Alias: |
| true | WARNING | no zero-width or otherwise non-standard characters |
| 2 | WARNING | leading spaces divide cleanly by this number |
| true | WARNING | filenames are lowercase (autocall on |
| true | WARNING | filenames contain no spaces |
| true | ERROR | no |
| 80 | WARNING | line length. Off with |
Off by default - switch on with :
true| Setting | Default | Severity | What it checks |
|---|---|---|---|
| false | WARNING | comment statements of the form |
| false | WARNING | a libref the file assigns and never uses |
| false | WARNING | macros carry the options in |
| | WARNING | line endings conform to |
Supporting settings (not rules):
| Setting | Default | Purpose |
|---|---|---|
| | paths to skip. Files in |
| | 4-hex-digit strings such as |
| see below | header template applied to a file with no |
| | per-rule override of |
| 80 | line limit inside the header |
| 80 | line limit inside |
| | options required by |
| | librefs |
规则分为两组,关闭某条规则的方式取决于它属于哪一组。
默认开启——除另有说明外,使用 关闭:
false| 设置 | 默认值 | 严重程度 | 检查内容 |
|---|---|---|---|
| true | WARNING | 文件以 |
| true | WARNING | |
| true | WARNING | macro 定义时带括号,且 |
| true | WARNING | 不在另一个 macro 内定义 |
| true | WARNING | 参数名中没有空格( |
| true | WARNING | 文件调用的每个 macro 都在文件或其 header 中声明 |
| true | WARNING | |
| true | WARNING | 没有以空白字符结尾的行 |
| true | WARNING | 不包含 tab 字符。别名: |
| true | WARNING | 不包含 zero-width 或其他非标准字符 |
| 2 | WARNING | 前导空格数可被该数字整除 |
| true | WARNING | 文件名使用小写( |
| true | WARNING | 文件名不包含空格 |
| true | ERROR | 不包含 |
| 80 | WARNING | 行长度。使用 |
默认关闭——使用 开启:
true| 设置 | 默认值 | 严重程度 | 检查内容 |
|---|---|---|---|
| false | WARNING | |
| false | WARNING | 文件赋值后从未使用的 libref |
| false | WARNING | macro 包含 |
| | WARNING | line ending 符合 |
支持性设置(并非规则):
| 设置 | 默认值 | 用途 |
|---|---|---|
| | 要跳过的路径。 |
| | 4 位十六进制字符串,例如 |
| 见下文 | 应用于没有 |
| | 按规则名称覆盖 |
| 80 | header 内的行长度限制 |
| 80 | |
| | |
| | |
Severity and exit codes
严重程度与退出码
Every rule has three states: off, WARNING, or ERROR. returns a non-zero exit code only when an ERROR is reported - warnings leave the exit code at 0, so a hook or pipeline keyed on the exit code passes a file that only warns. is the only rule that ships as ERROR.
sasjs lintnoEncodedPasswordsseverityLevel"warn""error"json
{
"severityLevel": {
"maxLineLength": "error",
"hasDoxygenHeader": "warn"
}
}每条规则都有三种状态:off、WARNING 或 ERROR。 仅在报告 ERROR 时返回非零退出码——warning 会使退出码保持为 0,因此以退出码为依据的 hook 或 pipeline 会放行仅产生 warning 的文件。 是唯一内置为 ERROR 的规则。
sasjs lintnoEncodedPasswordsseverityLevel"warn""error"json
{
"severityLevel": {
"maxLineLength": "error",
"hasDoxygenHeader": "warn"
}
}Per-file overrides
按文件覆盖配置
A file adjusts the rules that apply to itself with a block in its Doxygen header:
@sasjslintsas
/**
@file
@brief Calls the settlement API
@sasjslint {"maxHeaderLineLength": 200, "noUndeclaredMacros": false}
<h4> SAS Macros </h4>
@li mf_trim.sas
**/- Scalars replace, arrays are additive (,
allowedGremlins,requiredMacroOptionsgain entries rather than replacing), objects merge key by key (ignoredLibnames).severityLevel - The override applies to the formatter as well, so honours it - a file that switches
sasjs lint fixoff keeps the header entries it would otherwise lose.noUnusedMacros - Only the header is read, and only a line that starts with the tag followed immediately by . A malformed or unterminated block is ignored rather than raised.
{ - The header wins over the configuration the caller passes.
- is the one setting a header cannot use: it is evaluated before the file is read.
ignoreList - Place the block outside the section, which the formatter rewrites.
<h4> SAS Macros </h4>
文件可以通过 Doxygen header 中的 block 调整适用于自身的规则:
@sasjslintsas
/**
@file
@brief Calls the settlement API
@sasjslint {"maxHeaderLineLength": 200, "noUndeclaredMacros": false}
<h4> SAS Macros </h4>
@li mf_trim.sas
**/- 标量值会被替换,数组会累加(、
allowedGremlins、requiredMacroOptions会增加条目,而不是替换原值),对象则按 key 逐项合并(ignoredLibnames)。severityLevel - 覆盖项也适用于 formatter,因此 会遵循该配置——关闭
sasjs lint fix的文件会保留原本会被移除的 header 条目。noUnusedMacros - 仅读取 header,并且只识别以 tag 开头且紧接着 的行。格式错误或未终止的 block 会被忽略,而不会抛出错误。
{ - Header 的优先级高于调用方传入的配置。
- 是 header 无法使用的唯一设置,因为它会在读取文件之前完成求值。
ignoreList - 请将 block 放在 section 之外,因为 formatter 会重写该 section。
<h4> SAS Macros </h4>
The formatter
Formatter
The formatter applies the fixable rules on save. Implemented today:
- add the macro name to
%mend - add a Doxygen header template when the file has none
- add macros a file uses to , de-duplicated and sorted
<h4> SAS Macros </h4> - remove macros from that section that the file does not use
- remove trailing spaces
Not implemented (do not promise these): tabs to spaces, gremlin removal, line-ending correction, converting single-asterisk comments to block comments.
defaultHeader{lineEnding}\njson
{
"defaultHeader": "/**{lineEnding} @file{lineEnding} @brief Our Company Brief{lineEnding}**/"
}Formatter 会在保存时应用可修复的规则。目前已实现:
- 为 添加 macro 名称
%mend - 当文件没有 Doxygen header 时,添加 Doxygen header 模板
- 将文件使用的 macros 添加到 ,并去重和排序
<h4> SAS Macros </h4> - 从该 section 中移除文件未使用的 macros
- 移除行尾空格
尚未实现(不要承诺支持这些功能):将 tab 转换为空格、移除 gremlin、修正 line ending、将 single-asterisk comments 转换为 block comments。
defaultHeader{lineEnding}\njson
{
"defaultHeader": "/**{lineEnding} @file{lineEnding} @brief Our Company Brief{lineEnding}**/"
}Library API
Library API
ts
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?),lintFolderlintProject - ,
formatText(text, configuration?),formatFile,formatFolderformatProject - builds a config from a plain object;
new LintConfig(json)returns a new config with an override merged inconfig.override(json) - carries
Diagnostic,message,lineNumber,startColumnNumberandendColumnNumberseverity - is
Severity(0),Info(1),Warning(2)Error
Passing an explicit matters when the caller knows the rules - a server or editor linting a buffer with no path on disk cannot rely on the working-directory walk.
LintConfigts
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?)、lintFolderlintProject - 、
formatText(text, configuration?)、formatFile、formatFolderformatProject - 根据普通对象构建配置;
new LintConfig(json)返回合并了 override 的新配置config.override(json) - 包含
Diagnostic、message、lineNumber、startColumnNumber和endColumnNumberseverity - 包括
Severity(0)、Info(1)、Warning(2)Error
当调用方了解相关规则时,传入显式的 很重要——server 或 editor 对磁盘上没有 path 的 buffer 进行 Lint 时,无法依赖 working directory 查找配置。
LintConfigPitfalls
常见陷阱
- Off means something different per rule. The on-by-default rules switch off with ;
falseswitches off withmaxLineLength;0switches off withlineEndings; the off-by-default rules ignore"off"(it is the same as leaving them out) and needfalse. Getting this wrong is the most common reason a rule "does not work".true - and
maxHeaderLineLengthare inert whenmaxDataLineLengthis off, because they are scoped to it. The effective limit is the higher of the two values, so setting one lower thanmaxLineLengthis silently ignored.maxLineLength - plus the formatter removes
noUnusedMacrosdependencies. Those entries are consumed by the compiler, so a macro referenced only indirectly - through a dynamic@licall, or supplied by an%&named program - loses its declaration and the job can fail to assemble. Switch the rule off for the project or the file when that applies.%include - does not see
noUndeclaredMacros. Macros that ship with SAS are always treated as declared (the list is generated fromSASAUTOS), but macros made available through@sasjs/sas-languageare invisible to the linter and are reported. Declare them in the header or switch the rule off.SASAUTOS - is deliberately generous. A libref counts as used if it appears anywhere outside a
noUnusedLibnamesstatement - including inside a double-quoted string, as inLIBNAME. It is off by default because a file is not a complete job, and it has no formatter fix.pathname("outData", "L") - An empty is not the same as no
.sasjslintonly for settings whose default was previously applied conditionally. Since 4.0.0 an absent.sasjslintormaxLineLengthfalls back to its documented default (80, andhasMacroNameInMend), so a project that relied on omitting them to disable those rules must now settrueor"maxLineLength": 0."hasMacroNameInMend": false - entries are strings, not numbers:
allowedGremlins, exactly four hex digits."0x0080" - The CLI output is a box-drawn table. It is meant for a terminal; do not paste it into a plain-text or ASCII-only document without expecting the border glyphs to be a problem.
- with no subcommand behaves as
sasjs lint, linting everysasjs lint findfile in the project..sas
- Off 对不同规则的含义不同。 默认开启的规则使用 关闭;
false使用maxLineLength关闭;0使用lineEndings关闭;默认关闭的规则会忽略"off"(等同于不配置),必须使用false开启。配置错误是规则“不起作用”的最常见原因。true - 当 关闭时,
maxLineLength和maxHeaderLineLength不会生效,因为它们的作用域限定于maxDataLineLength。实际限制取两者中的较高值,因此将其中一个值设置得低于maxLineLength会被静默忽略。maxLineLength - 启用 后,formatter 会移除
noUnusedMacros依赖项。 这些条目供 compiler 使用,因此仅通过 dynamic@licall 间接引用,或由%&named program 提供的 macro 会失去声明,job 可能无法 assemble。遇到这种情况时,请对项目或文件关闭该规则。%include - 无法识别
noUndeclaredMacros。 SAS 随附的 macros 始终被视为已声明(该列表根据SASAUTOS生成),但通过@sasjs/sas-language提供的 macros 对 linter 不可见,因此会被报告。可以在 header 中声明它们,或关闭该规则。SASAUTOS - 有意采用较宽松的判断。 只要 libref 出现在
noUnusedLibnamesstatement 之外的任何位置,就会被视为已使用,包括双引号字符串中,例如LIBNAME。该规则默认关闭,因为单个文件并非完整 job,而且它没有对应的 formatter 修复。pathname("outData", "L") - 空的 与没有
.sasjslint并不相同,这种差异仅适用于以前按条件应用默认值的设置。从 4.0.0 开始,缺失的.sasjslint或maxLineLength会回退到文档规定的默认值(分别为 80 和hasMacroNameInMend),因此过去依靠省略这些配置来禁用规则的项目,现在必须设置true或"maxLineLength": 0。"hasMacroNameInMend": false - 的条目是字符串,不是数字:例如
allowedGremlins,且必须恰好包含 4 位十六进制数字。"0x0080" - CLI 输出是带方框线条的表格。 它专为 terminal 设计;不要在未考虑边框字形问题的情况下直接粘贴到纯文本或仅支持 ASCII 的文档中。
- 不带 subcommand 运行 时,其行为与
sasjs lint相同,会检查项目中的所有sasjs lint find文件。.sas
Verification
验证
- Confirm a rule fires: put the offending construct in a file and run
.sas. A hit prints a row with the message and aterminal(command="npx sasjs lint")position; no row means the rule is off or the construct is excluded (check the config first, then the rule's exclusions).[line, column] - Confirm a config change took effect: run before and after editing
npx sasjs lint, and compare the rows. A rule that is off by default needs.sasjslint, nottrue.false - Confirm the formatter's effect: run and inspect the diff - the
npx sasjs lint fixsection and<h4> SAS Macros </h4>lines are the ones it rewrites.%mend - Confirm a per-file override: add the block, run
@sasjslint, and check that the rule it switches off no longer reports for that file while still reporting for its neighbours.sasjs lint
- 确认规则是否触发:将存在问题的代码放入 文件,并运行
.sas。如果命中,会输出一行消息及其terminal(command="npx sasjs lint")位置;如果没有输出,则说明规则已关闭或该代码被排除(先检查配置,再检查规则的 exclusions)。[line, column] - 确认配置变更是否生效:在编辑 前后分别运行
.sasjslint,并比较输出结果。默认关闭的规则需要设置为npx sasjs lint,而不是true。false - 确认 formatter 的效果:运行 并检查 diff——
npx sasjs lint fixsection 和<h4> SAS Macros </h4>行是它会重写的内容。%mend - 确认按文件覆盖配置:添加 block,运行
@sasjslint,并检查该规则关闭后不再为该文件报告问题,同时仍会为相邻文件报告。sasjs lint