Skip to content

Output and exit codes

Markdown for agents

Each example on this page comes from one repository. Its JSON config has one table. The file src/shared/label.ts imports a file of the feature cart, and the table refuses it:

archangel.config.json
{
"$schema": "./node_modules/@enhance-eng/archangel/archangel.schema.json",
"parts": {
"feature": "src/features/{feature}/**",
"shared": "src/shared/**"
},
"rules": [
{
"kind": "mayImport",
"name": "app",
"rows": { "feature": ["shared"], "shared": [] },
"because": "Shared code never depends on a feature."
}
]
}

The default format, for people. Each problem shows its code, its message, the line of source code with a mark under the import, what to do, the rule and your reason.

Terminal
$ npx archangel check
error[may-import]: shared may not import feature, through "../features/cart/total": shared may import only itself
--> src/shared/label.ts:1:23
|
1 | import { total } from '../features/cart/total';
| ^^^^^^^^^^^^^^^^^^^^^^^^ shared may import only itself
|
= help: remove the import, or move the code to a file whose row permits the import
= note: rule: app.shared
= note: because: Shared code never depends on a feature.
checked 2 files and 1 imports: 1 problem
For what a code means, run `archangel docs may-import`.
Line What it is
error[may-import]: ... The diagnostic code and the message. archangel docs <code> explains the code.
--> src/shared/label.ts:1:23 The file, the line and the column.
= help: ... What to do.
= note: rule: ... The name of the rule, for off, an exception or the baseline.
= note: because: ... Your reason, from because.
= note: see: ... Your link, from link.

There are no warnings. Each problem is an error.

One JSON document, for programs. check, test, rules, explain and why write JSON.

Terminal
$ npx archangel check --format json
{
"schemaVersion": 1,
"diagnostics": [
{
"code": "may-import",
"severity": "error",
"rule": "app.shared",
"file": "src/shared/label.ts",
"line": 1,
"column": 23,
"span": [
22,
46
],
"specifier": "../features/cart/total",
"message": "shared may not import feature, through \"../features/cart/total\": shared may import only itself",
"help": "shared may import only itself",
"reason": "Shared code never depends on a feature.",
"link": null,
"id": {
"rule": "app.shared",
"file": "src/shared/label.ts",
"specifier": "../features/cart/total",
"occurrence": 0
},
"cycle": [],
"group": [],
"chain": [],
"fix": null
}
],
"summary": {
"files": 2,
"imports": 1,
"warnings": 0,
"exceptions": {},
"baselined": {},
"permittedUnresolved": 0,
"unfollowed": 0,
"unfollowedImports": [],
"linksNotFollowed": [],
"nestedRepositories": [],
"presetNamesMatchingNothing": [],
"work": {
"filesFound": 2,
"filesRead": 2,
"bytesRead": 113,
"filesParsed": 2,
"importsFound": 1,
"resolutions": 1,
"filesClassified": 4,
"patternPasses": 4,
"ruleEvaluations": 1,
"cycleFilesVisited": 0,
"transitiveFilesVisited": 0,
"structureEvaluations": 0,
"folderEvaluations": 0,
"exportEvaluations": 0,
"examplesChecked": 0,
"filesWritten": 0,
"pluginRecordsSent": 0,
"pluginChecks": 0,
"packageModulesParsed": 0
},
"rowsWithNoFile": []
}
}
  • schemaVersion is the version of the document.
  • Each diagnostic has id, the identity of the problem: the rule, the file, the specifier and the occurrence. It does not hold the line, so it stays the same when the line moves.
  • cycle, group and chain hold the files of a cycle or of a chain of imports.
  • fix holds a fix that archangel checked, when it has one.
  • severity is error, or warning for a rule with severity: 'warn'. summary.warnings counts the warnings.
  • summary.unfollowedImports lists each import whose specifier is computed, by file and line. The text output lists them under the count.
  • summary.work holds the work counts.
  • test --format json lists each failed example and each unproven rule under diagnostics too.
  • When the config stops a command, the JSON document goes to standard output, as each other document does.

explain with a line writes each rule and its verdict:

Terminal
$ npx archangel explain src/shared/label.ts:1 --format json
{
"schemaVersion": 1,
"file": "src/shared/label.ts",
"line": 1,
"imports": [
{
"specifier": "../features/cart/total",
"line": 1,
"target": {
"file": "src/features/cart/total.ts"
},
"from": {
"part": "shared",
"instance": [],
"kinds": []
},
"to": {
"part": "feature",
"instance": [
"cart"
],
"kinds": []
},
"rules": [
{
"rule": "app.feature",
"verdict": "skips",
"reason": "the file is not in feature",
"permittedBy": null
},
{
"rule": "app.shared",
"verdict": "refuses",
"reason": "feature is not in its row (shared may import only itself)",
"permittedBy": null
},
{
"rule": "app.every-file-has-a-kind",
"verdict": "skips",
"reason": "this rule checks files, not imports",
"permittedBy": null
}
]
}
]
}

why writes the chain:

Terminal
$ npx archangel why src/shared/label.ts src/features/cart/total.ts --format json
{
"schemaVersion": 1,
"from": "src/shared/label.ts",
"to": "src/features/cart/total.ts",
"chain": [
{
"file": "src/shared/label.ts",
"line": 1,
"specifier": "../features/cart/total"
}
]
}

watch --format json writes one line of JSON for each check. Read Commands.

Writes each problem as a GitHub Actions annotation. GitHub shows it on its line in the pull request.

Terminal
$ npx archangel check --format github
::error file=src/shared/label.ts,line=1,col=23,endColumn=47,title=archangel app.shared::shared may not import feature, through "../features/cart/total": shared may import only itself%0Ahelp: shared may import only itself%0Areason: Shared code never depends on a feature.
checked 2 files and 1 imports: 1 problem

Use it in a workflow:

- run: npx archangel check --format github
- run: npx archangel test --format github

Read Use archangel in CI.

Writes a SARIF 2.1.0 log, for GitHub code scanning or another tool that reads SARIF. The log lists each diagnostic code as a rule, with the text of archangel docs. Each result has the identity of its problem, so an alert stays the same when its line moves.

Terminal window
npx archangel check --format sarif > archangel.sarif

Adds the work that archangel did, and the time of each stage. The work counts are:

Count What it counts
files found, files read, bytes read, files parsed the source files
imports found, resolutions the imports, and the imports that archangel resolved
files classified, pattern passes the work to find the parts and groups of each file
rule evaluations the times that archangel applied a rule
cycle files visited, transitive files visited the files that the search for cycles and chains visited
structure evaluations, folder evaluations, export evaluations the work of structure, folder and export rules
examples checked, files written the examples of test. files written is always 0.
plugin records sent, plugin checks the work of plugins
package modules parsed the package files that archangel read for the names that a namespace or dynamic import brings in

The JSON output holds the same counts in summary.work. The times are only in --stats, because a time changes from run to run.

Code Meaning
0 The command succeeded, and found no problem, or only warnings.
1 Archangel found a problem, or the answer is no.
2 Archangel could not run: the config, the baseline or the command line has an error.

A warning does not change the exit code, unless check --max-warnings <count> is given and there are more warnings than the count: then the check exits with 1.

A config error stops the check before archangel reads the code. It exits with 2:

Terminal
$ npx archangel check
error[rule-unknown-element]: rules[0].files: the config declares no part or group "shraed"; did you mean "shared"? the declared names are shared; a kind has a meaning only in its table, and a path pattern has a `/` or a `*`
--> archangel.config.json:6:37