# Development

Repolinter is broken down into 7 main components, each of which is contained in a subfolder:
 * **Axioms** - located under [axioms](../../axioms)
 * **CLI** - located under [bin](../../bin)
 * **Rules** - located under [rules](../../rules)
 * **Fixes** - located under [fixes](../../fixes)
 * **Formatters** - located under [formatters](../../formatters)
 * **Utilities** - located under [lib](../../lib)
 * **Built-In Rulesets and Schema** - located under [rulesets](../../rulesets)

## Axioms

Axioms are registered in [axioms/axioms.js](../../axioms/axioms.js) and in [rulesets/schema.json](../../rulesets/schema.json). An axiom implementation consists of a module with the following interface:
```TypeScript
async (fs: FileSystem) => Result
```
Where fs is a [`FileSystem`](../../lib/file_system.js) scoped to the current repository.

The contents of the result should be an array of targets where `t.path` is a target that the axiom has determined is valid (ex. for language detecting axiom a possible result could be `new Result('', [{ path: 'javascript' passed: true }, { path: 'typescript', passed: true}], true)`). If the axiom fails to execute, it should return a failing result with an error message included instead of throwing an error.

## Rules

A rule consists of two parts: a JavaScript module, which determines the rule's functionality and a JSON schema, which validates the rule's configuration options in rulesets. Rules are registered in [rules/rules.js](../../rules/rules.js) and [rulesets/schema.json](../../rulesets/schema.json). Rules are also documented under [rules.md](./rules.md).

### Rule Configuration JSON Schema

The configuration JSON schema determines how `rule.options` should be validated for this rule. All [JSON Schema](https://json-schema.org/) tools supported by [AJV](https://ajv.js.org/) all available, with a few important caveats:
 * The file itself should always be named `<rule-name>-config.json` and be located under the `rules` folder.
 * The top-level type should always be `object`.
 * The `$id` field should be an absolute raw.githubusercontent.com URL to where the schema is hosted on GitHub (ex. https://raw.githubusercontent.com/todogroup/repolinter/master/rules/apache-notice-config.json). This allows IDEs such as VSCode to apply the schema via a URL.

To get started, you can use the following template:
```JSON
{
    "$schema": "http://json-schema.org/draft-07/schema",
    "$id": "https://raw.githubusercontent.com/todogroup/repolinter/master/rules/<rule-name>-config.json",
    "type": "object",
    "properties": {}
}
```

Due to limitations with JSON Schema, you also must register your rule configuration schema in [rulesets/schema.json](../../rulesets/schema.json) by adding the following item to the list under `root.then.properties.rules.properties.rule.allOf`:
```JSON
{ "if": { "properties": { "type": { "const": "<rule-name>" } } }, "then": { "properties": { "options": { "$ref": "../rules/<rule-name>-config.json" } } } }
```

### JavaScript Implementation

A rule implementation consists of a module with the following interface:
```TypeScript
async (fs: FileSystem, opts /* type determined by your JSON schema */) => Result
```
Where fs is a [`FileSystem`](../../lib/file_system.js) scoped to the current repository and `opts` is the options provided by the ruleset.

A rule implementation is encouraged to use `Result#targets` to show the individual files/patterns checked when processing the rule. Including filenames in `Result#message` is discouraged as it makes formatting difficult. If a rule fails to execute, it should throw an error.

## Fixes

A fix, similar to a rule, consists of two parts: a JavaScript module, which determines the fix's functionality and a JSON schema, which validates the fix's configuration options in rulesets. Fixes are registered in [fixes/fixes.js](../../fixes/fixes.js) and [rulesets/schema.json](../../rulesets/schema.json). Fixes are also documented under [fixes.md](./fixes.md).

### Fix Configuration JSON Schema

Fix JSON schemas work identically to [rule JSON schemas](#rule-configuration-json-schema), with the only difference the respective names and paths.

To get started, you can use the following template:
```JSON
{
    "$schema": "http://json-schema.org/draft-07/schema",
    "$id": "https://raw.githubusercontent.com/todogroup/repolinter/master/fixes/<fix-name>-config.json",
    "type": "object",
    "properties": {}
}
```

Similar as with rules, you must register the fix schema in [rulesets/schema.json](../../rulesets/schema.json) by adding the following item to the list under `root.then.properties.rules.properties.fix.allOf` (note that this is a different list than the rule registration list):
```JSON
{ "if": { "properties": { "type": { "const": "<fix-name>" } } }, "then": { "properties": { "options": { "$ref": "../rules/<fix-name>-config.json" } } } }
```

### JavaScript Implementation

Unlike rules, a fix implementation consists of a module with the following interface:
```TypeScript
async (fs: FileSystem, options /* Type determined by your JSON schema */, targets: string[], dryRun: boolean) => Result
```
Where fs is a [`FileSystem`](../../lib/file_system.js) scoped to the current repository, `opts` is the options provided by the ruleset, `targets` are filepaths which did not pass the rule associated with this fix, and `dryRun` determines if the fix is allowed to make changes to the repository.

The fix implementation is encouraged to use `Result#targets` to show the individual files/patterns changed. If the fix fails to execute, it should either return a failed result or throw an error.

## Formatters

Formatters are exported by [index.js](../../index.js) and manually called by the [CLI](../../bin/repolinter.js). A formatter implementation consists of the following interface:
```TypeScript
interface Formatter {
  formatOutput(output: LintResult, dryRun: boolean): string
}
```
Formatters do not print to `STDOUT` instead choosing to return the output as a string.

If needed, a formatter can accept extra configuration from the ruleset through the `formatOptions` property, which will be directly passed through to `LintResult#formatOptions`. These options are not typed and are formatter dependent.
