> ## Documentation Index
> Fetch the complete documentation index at: https://docs.commonality.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating checks

Checks can created to standardize any existing configuration or workflow.

All checks have two required properties:

1. `message`: The default message that will be shown for the check in the CLI and Commonality Studio
2. `validate`: A function that returns a boolean or a promise that resolves to a boolean. This function is used to determine if the check passes or fails.

### Check context

Checks are run within the context of each package directory.
In the example below, we're accessing the [context object](/reference/check-object#parameters) that contains metadata about the package to validate that it has a codeowner.

```typescript .commonality/has-codeowner.ts theme={null}
import type { Check } from 'commonality';

export default {
  message: 'Package has least one codeowner',
  validate: (ctx) => ctx.codeowners.length > 0,
} satisfies Check;
```

### Checking files

Commonality provides utilities like [json](/reference/json) and [text](/reference/text) that make it easy to read and write to files within checks.

Below is an example of a check that ensures a package has a `build` script with a value of `tsc --build` in it's `package.json`.

```typescript .commonality/has-build-script.ts theme={null}
import type { Check } from 'commonality';
import { json } from 'commonality';

export default {
  message: 'Package must have a "build" script',
  validate: async (ctx) => {
    return json(ctx.package.path, 'package.json').contains({
      scripts: { build: 'tsc --build' },
    });
  },
} satisfies Check;
```

The following output will be shown if the check fails:

```text theme={null}
✓ fail Package has a "build" script
```

### Customizing messages

The default message can be overriden by returning an object from the validate function to provide additional context about a failed check.

We can modify the previous example to provide a more detailed error messages.

```ts .commonality/has-build-script.ts theme={null}
import type { Check } from 'commonality';
import { json, diff } from 'commonality';

export default {
  message: 'Package must have a "build" script',
  validate: async (ctx) => {
    const packageJson = await json(ctx.package.path, 'package.json').get();

    if (!packageJson) {
      return {
        message: 'package.json does not exist',
        path: 'package.json',
      };
    }

    if (packageJson?.scripts?.build !== 'tsc --build') {
      return {
        message:
          'package.json does not have a "build" script with value "tsc --build"',
        path: 'package.json',
        suggestion: diff(
          { scripts: packageJson.scripts },
          { scripts: { build: 'tsc --build' } },
        ),
      };
    }

    return true;
  },
} satisfies Check;
```

The [diff](/reference/check-object#diff) utility can be used to suggest file edits needed to pass checks. The `suggestion` property is also useful for showing how a [fix](/reference/check-object#fix) function will modify a file.

The [diff](/reference/check-object#diff) function will return a pretty printed string where unique properties and values in the second argument appear in red and are prefixed with `+`."

The following output will be shown if the check fails:

```text theme={null}
✓ warn package.json does not have a "build" script with value "tsc --build"
│      packages/pkg-a/package.json
│        Object {
│            "scripts": {
│              "lint": "eslint .",
│      +       "build": "tsc --build",
│        }
```

### Composing checks

Since checks are just objects, you can create functions that return checks to make them re-usable in a variety of different scenarios.

Previously, we wrote a lot of code just to validate a single script existed, now we can re-use this check to validate the existence of any script.

```ts .commonality/has-script.ts theme={null}
import type { Check } from 'commonality';
import { json, diff } from 'commonality';

export const hasScript = (name: string, value: string): Check =>
  ({
    message: `Package must have a "${name}" script`,
    validate: async (ctx) => {
      const packageJson = await json(ctx.package.path, 'package.json').get();

      if (!packageJson) {
        return {
          message: 'package.json does not exist',
          path: 'package.json',
        };
      }

      if (packageJson?.scripts[name] !== value) {
        return {
          message: `package.json does not have a "${name}" script with value "${value}"`,
          path: 'package.json',
          suggestion: diff(
            { scripts: packageJson.scripts },
            { scripts: { [name]: value } },
          ),
        };
      }

      return true;
    },
  }) satisfies Check;
```

## Auto-fixable checks

Check failures help enforce standardization at scale but can add friction to developer workflows.
To solve this, you can make any check auto-fixable, making it easy for developers to adhere to standards without needing to manually fix issues.

To make a check fixable just add a [fix](/reference/check-object#fix) property to your check. This function will only run against a package if the result of [validate](/reference/check-object#validate) is not `true`.

Commonality provides utilities like [json](/reference/check-object#json), and [text](/reference/check-object#text) to make it easy to read and write files within fix functions.

```ts .commonality/has-script.ts theme={null}
import type { Check } from 'commonality';
import { json, diff } from 'commonality';

export const hasScript = (name: string, value: string): Check =>
  ({
    // ...check logic,
    fix: async (ctx) => {
      await json(ctx.package.path, 'package.json').merge({
        scripts: {
          [name]: value,
        },
      });
    },
  }) satisfies Check;
```
