hana-linter
v1.1.0
Published
Linter for SAP HANA artifacts in an SAP CAP project
Readme
hana-linter
Naming-convention lint for SAP HANA artifacts in CAP projects.
⚠️ Work in progress. This project is under active development. APIs, configuration options, and supported artifact types may change between releases. See Parser Status for the current state of content extraction support.
NPM package • Report issue • Releases
Lint SAP HANA artifact file names and content identifiers in CAP projects using configurable regex-based naming rules.
Why
Teams often rely on naming conventions for HANA artifacts such as tables and views. When these conventions are enforced manually, drift is common and code reviews become noisy.
hana-linter helps you:
- enforce naming standards consistently
- catch violations early in local development and CI
- apply different rules per file extension
- keep naming policy in version control via a single config file
How It Works
hana-linter reads a .hana-linter.json file and validates artifact file names against rules grouped by extension.
It supports two lint modes:
- Full scan mode: no file arguments, recursively scans
rootDir - File-list mode: pass file paths, only those files are validated
Rule groups per extension:
groups.all: every rule must match (AND)groups.any: at least one rule must match (OR)
You can define extension: "*" as a shared rule set. Its rules are applied to every file extension and are merged with any extension-specific rule set.
Content-based linting uses Chevrotain-powered lexers and CST parsers to reliably extract identifiers from HANA artifact files. This approach correctly handles block and line comments, multi-line definitions, quoted identifiers, and HANA-specific DDL constructs — without the false positives and false negatives that ad-hoc regex scanning produces.
Parser Status
Work in progress. Not all artifact types have been migrated to the Chevrotain-based parsing infrastructure yet.
| Artifact extension | Content extractor | Status |
| ------------------------- | ---------------------- | ---------------------- |
| .hdbtable | Chevrotain lexer + CST | ✅ Migrated |
| .hdbview | Chevrotain lexer + CST | ✅ Migrated |
| .hdbprocedure | Chevrotain lexer + CST | ✅ Migrated |
| .hdbfunction | Chevrotain lexer + CST | ✅ Migrated |
| .hdbtabletype | — | ❌ Not yet implemented |
| .hdbcalculationview | — | ❌ Not yet implemented |
| .hdbanalyticalprivilege | — | ❌ Not yet implemented |
| .hdbrole | — | ❌ Not yet implemented |
| .hdbsequence | — | ❌ Not yet implemented |
| .hdbconstraint | — | ❌ Not yet implemented |
| .hdbschedulerjob | — | ❌ Not yet implemented |
| .hdbindex | — | ❌ Not yet implemented |
| .hdbtrigger | — | ❌ Not yet implemented |
- Not implemented:
contentRuleSetstargeting these extensions will silently return no results — no identifiers are extracted and no content issues are raised.
Install
Local (recommended for projects)
npm install --save-dev hana-linterRun with:
npx hana-linterGlobal
npm install -g hana-linterQuick Start
- Generate a default config in your project root:
hana-linter init- Run the linter:
hana-linter- If needed, regenerate and overwrite config:
hana-linter init --forceCommands
hana-linter
Run lint using .hana-linter.json from the current working directory.
hana-linterLint specific files only:
hana-linter db/src/T_CUSTOMER.hdbtable db/src/V_ACTIVE_USERS.hdbviewUse a custom config path:
hana-linter --config ./config/.hana-linter.jsonhana-linter init
Create .hana-linter.json in the current working directory from the bundled default template.
hana-linter initOverwrite existing config:
hana-linter init --forceConfiguration
Create a .hana-linter.json file in your project root.
Configuration Fields
rootDir(string): directory to scan in full scan modeignoredDirectories(string[]): folder names ignored during recursive traversalextensionRuleSets(array): rule definitions grouped by file extensioncontentRuleSets(optional array): naming rules for identifiers extracted from file contents (for example table fields and procedure/function parameters)
Each extensionRuleSets item contains:
extension(string): target extension, for example.hdbtable; use*to target all extensionsfolderName(optional string): enforce that matching files are located in a folder with this name (at any depth underrootDir)groups.all(optional array): all rules must matchgroups.any(optional array): at least one rule must match
Each rule contains:
description(string): readable rule label for outputpattern(string): regex source (without/delimiters)flags(optional string): regex flags, for examplei,u,iu
At least one of groups.all or groups.any must be present for each extension.
When folderName is omitted, no folder-location enforcement is applied.
Each contentRuleSets item contains:
extension(string): target extension, for example.hdbtable; use*to target all extensionstarget(string): extracted identifier type to validate; one offield,inputParameter,outputParametergroups.all(optional array): all rules must matchgroups.any(optional array): at least one rule must match
Supported extractors in this version:
| target | Supported extensions | Extracted identifiers |
| ----------------- | -------------------- | ---------------------------------------------- |
| field | .hdbtable | Column names |
| field | .hdbview | Column aliases (explicit list or AS aliases) |
| inputParameter | .hdbprocedure | IN and INOUT parameters |
| inputParameter | .hdbfunction | IN parameters (functions accept IN only) |
| outputParameter | .hdbprocedure | OUT and INOUT parameters |
Default Config Example
{
"rootDir": "db",
"ignoredDirectories": ["node_modules", ".git", "gen"],
"extensionRuleSets": [
{
"extension": "*",
"groups": {
"all": [
{
"description": "Upper snake case only",
"pattern": "^[A-Z0-9]+(?:_[A-Z0-9]+)*$"
},
{
"description": "Max length 30",
"pattern": "^.{1,30}$",
"flags": "u"
}
]
}
},
{
"extension": ".hdbtable",
"folderName": "tables",
"groups": {
"any": [
{
"description": "Prefix T_",
"pattern": "^T_.+"
},
{
"description": "Prefix TX_",
"pattern": "^TX_.+"
}
]
}
},
{
"extension": ".hdbview",
"groups": {
"all": [
{
"description": "Starts with V_",
"pattern": "^V_.+"
}
]
}
}
],
"contentRuleSets": [
{
"extension": ".hdbtable",
"target": "field",
"groups": {
"all": [
{
"description": "Field names in uppercase snake case",
"pattern": "^[A-Z0-9]+(?:_[A-Z0-9]+)*$"
}
]
}
},
{
"extension": ".hdbprocedure",
"target": "inputParameter",
"groups": {
"all": [
{
"description": "Input parameters prefixed with IP_",
"pattern": "^IP_[A-Z0-9_]+$"
}
]
}
},
{
"extension": ".hdbprocedure",
"target": "outputParameter",
"groups": {
"all": [
{
"description": "Output parameters prefixed with OP_",
"pattern": "^OP_[A-Z0-9_]+$"
}
]
}
},
{
"extension": ".hdbview",
"target": "field",
"groups": {
"all": [
{
"description": "View column aliases in uppercase snake case",
"pattern": "^[A-Z0-9]+(?:_[A-Z0-9]+)*$"
}
]
}
}
]
}Exit Codes
0: lint passed orinitcompleted successfully1: lint violations found or command failed
This makes the CLI suitable for CI pipelines.
CI Example
name: lint-hana-names
on: [push, pull_request]
jobs:
hana-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx hana-linterRequirements
- Node.js >= 14
- npm >= 7
Contributing
Contributions are welcome. Please open an issue or submit a pull request.
License
MIT
