# @hyperfrontend/linter

<p align="center">
  <a href="https://www.hyperfrontend.dev/docs/libraries/linter.md">
    <img width="640" height="360" src="https://www.hyperfrontend.dev/media/linter-mend/hero.gif" alt="A TypeScript file, cart.ts, sits on a sheet while a beam reads it line by line; as each line is read its node appears beside the sheet at the same height, nested the way the code nests. Three rules find something: var is underlined and no-var lands beside its node, let count is underlined with prefer-const, and !!item.price with no-extra-boolean-cast. The --fix flag lights on the hf-lint chip and the fixes land one by one: let total, const count and item.price are written in place, each underline retracts, each tag gets a check, and a check lands beside the file's name">
  </a>
</p>

A linter for TypeScript, JavaScript, JSX, JSON and Markdown with its own parsers and no external runtime packages.

## What is @hyperfrontend/linter?

[`@hyperfrontend/linter`](https://www.hyperfrontend.dev/docs/libraries/linter.md) checks TypeScript, JavaScript, JSX, JSON and Markdown against rules you choose, and fixes what the rules know how to fix. It parses every file with parsers of its own and installs no dependencies.

It is not a drop-in [ESLint](https://eslint.org/). It reimplements a selection of rules from ESLint and well-known plugins, under a configuration that reads like ESLint's flat config, and it does not load ESLint plugins, parsers or shareable configs.

### Key Features

- **[Packaged rules](https://www.hyperfrontend.dev/docs/libraries/linter/rules.md)** - Core JavaScript, TypeScript, JSDoc, React, React hooks and package.json rules, and a set of conventions
- **[Presets to start from](https://www.hyperfrontend.dev/docs/libraries/linter/#api-CODE_RULES)** - The rules the upstream recommended configurations turn on, as plain objects you spread into a block
- **One parse, one traversal** - The listeners of every rule in effect for a file run in a single walk of its tree
- **[Rules of your own](https://www.hyperfrontend.dev/docs/libraries/linter/#api-defineRule)** - Register them under a prefix with [`families`](https://www.hyperfrontend.dev/docs/libraries/linter/#api-ConfigBlock-prop-families) and test them with [`testRule`](https://www.hyperfrontend.dev/docs/libraries/linter/testing.md)
- **Fixes and suggestions** - `--fix` applies fixes; suggestions, for changes that need your judgement, appear in the JSON report
- **No dependencies** - The package is self-contained; linting loads nothing from npm

### Architecture Highlights

Every file is parsed once and every rule's listeners run in one traversal of it; the services rules share, such as scopes and code paths, are computed once per file and never rebuilt. The [architecture guide](https://www.hyperfrontend.dev/docs/libraries/linter/architecture.md) covers the parsers, the shared services and the fix loop.

## Why Use @hyperfrontend/linter?

### Fewer packages in the toolchain

A linting setup built on ESLint brings a parser, plugins and their dependencies into every install. This package is one dependency with nothing beneath it, for a project whose checks fit its rules.

### Rules you can read and own

Each rule is one module with its specification beside it. When the packaged rules stop short, [write and test a rule of your own](https://www.hyperfrontend.dev/docs/guides/write-and-test-a-custom-lint-rule.md) with the same API they use.

## Installation

```bash
npm install --save-dev @hyperfrontend/linter
```

Requires Node.js 24 or later, which loads the TypeScript configuration file without a compiler.

## Quick Start

Create `lint.config.ts` at the project root:

```typescript
import { CODE, CODE_RULES, on } from '@hyperfrontend/linter'

export default [
  { ignores: ['dist/', 'coverage/'] },
  { files: CODE, rules: CODE_RULES },
  { files: ['src/**/*.ts'], rules: on('no-enum import-order') },
]
```

Then lint and fix from the project root:

```bash
npx hf-lint
npx hf-lint src --fix
```

[Lint a project with @hyperfrontend/linter](https://www.hyperfrontend.dev/docs/guides/lint-a-project-with-hyperfrontend-linter.md) covers configuration, directives, exit codes, CI and linting from code with [`lint`](https://www.hyperfrontend.dev/docs/libraries/linter/#api-lint).

## API Overview

| Entry                                                                           | What it is                                                                                                                                       |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [`@hyperfrontend/linter`](https://www.hyperfrontend.dev/docs/libraries/linter.md) | Running, configuration types and presets, [`defineRule`](https://www.hyperfrontend.dev/docs/libraries/linter/#api-defineRule) and the rule types |
| [`/helpers`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers.md)      | Tokens, scopes, JSON properties and positions, for rules of your own                                                                             |
| [`/testing`](https://www.hyperfrontend.dev/docs/libraries/linter/testing.md)      | [`testRule`](https://www.hyperfrontend.dev/docs/libraries/linter/testing/#api-testRule), which turns a rule's cases into `node:test` tests       |
| `hf-lint`                                                                       | The command line                                                                                                                                 |

Every rule, with its options and examples, is in the [rule catalogue](https://www.hyperfrontend.dev/docs/libraries/linter/rules.md). Moving from ESLint? [Replace ESLint with @hyperfrontend/linter](https://www.hyperfrontend.dev/docs/guides/replace-eslint-with-hyperfrontend-linter.md) starts with what does and does not carry over.

## Limitations

- No rule uses type information, and every packaged rule reads only the file it lints.
- No editor integration: run it from the terminal, a hook or CI.
- No result cache between runs.
- YAML files are read as plain lines for rules of your own; no packaged rule reads them.

## Acknowledgements

The packaged rules reimplement rules from [ESLint](https://eslint.org/), [typescript-eslint](https://typescript-eslint.io/), [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc), [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react), [eslint-plugin-react-hooks](https://react.dev/reference/eslint-plugin-react-hooks) and [eslint-plugin-package-json](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json), whose messages, options and test cases they follow. Their licences and the details are in [NOTICE.md](https://github.com/AndrewRedican/hyperfrontend/blob/main/libs/linter/NOTICE.md). How the linter came to be written is told in [I Replaced ESLint. The First Attempt Worked. I Threw It Away.](https://www.hyperfrontend.dev/articles/i-replaced-eslint-the-first-attempt-worked-i-threw-it-away.md)

## Compatibility

Requires Node.js 24 or later, on Linux or Windows. There is no browser build.

| Environment     | Supported |
| --------------- | :-------: |
| Node.js >= 24   |    ✅     |
| Modern Browsers |    ❌     |
| Web Workers     |    ❌     |

### Output Formats

| Format | File              | Tree-Shakeable |
| ------ | ----------------- | :------------: |
| ESM    | `index.mjs`       |       ✅       |
| CJS    | `index.cjs`       |       ❌       |
| Bin    | `bin/hf-lint.mjs` |       ❌       |

## Part of hyperfrontend

This library is part of the [hyperfrontend](https://github.com/AndrewRedican/hyperfrontend) monorepo.

**📖 [Full documentation](https://www.hyperfrontend.dev/docs/libraries/linter.md)**

## License

[MIT](https://github.com/AndrewRedican/hyperfrontend/blob/main/LICENSE.md)

## API Reference

Grouped by entry point. A declaration exported from several entry points is documented once, under the most specific one, and listed by name under the others.

### @hyperfrontend/linter

Lint from the command line or as a function, configure with ordered blocks in
`lint.config.ts`, select the packaged rules and presets, and define rules of your own.

#### Functions

##### defineRule

```typescript
function defineRule<Options, Node>(rule: RuleModule<Options, Node>): RuleModule<Options, Node>
```

Names a rule, for the type it gives a rule module's default export.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `rule` | `RuleModule<Options, Node>` | The id, language, fixability and listener factory |

**Returns** `RuleModule<Options, Node>`: The same object, typed as a rule module

**Example: Define a rule that reports debugger statements**

```ts
import { defineRule } from '@hyperfrontend/linter'

export default defineRule({
  id: 'local/no-debugger',
  language: 'code',
  fixable: false,
  create: (context) => ({
    DebuggerStatement(node) {
      context.report({ start: node.start, end: node.end, message: 'Remove the debugger statement.' })
    },
  }),
})
```

##### formatJson

```typescript
function formatJson(results: unknown): string
```

Formats results as JSON.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `results` | `unknown` | One result per file |

**Returns** `string`: One line of JSON

**Example: Print a report for another tool to read**

```ts
import { formatJson, lint } from '@hyperfrontend/linter'

const { results } = await lint({ cwd: process.cwd() })
process.stdout.write(`${formatJson(results)}\n`)
```

##### formatStylish

```typescript
function formatStylish(results: unknown): string
```

Formats results.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `results` | `unknown` | One result per file, in the order to print |

**Returns** `string`: The report, empty when no file has a problem

**Example: Print a report for a person to read**

```ts
import { formatStylish, lint } from '@hyperfrontend/linter'

const { results } = await lint({ cwd: process.cwd(), patterns: ['src'] })
process.stdout.write(formatStylish(results))
```

##### lint

```typescript
function lint(request: LintRequest): Promise<LintReport>
```

Lints files, or a text, with the configuration that applies to them.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `request` | `LintRequest` | The working directory, what to lint and how |

**Returns** `Promise<LintReport>`: The results and their totals

**Example: Lint a project and count its errors**

```ts
import { lint } from '@hyperfrontend/linter'

const report = await lint({ cwd: process.cwd(), patterns: ['src'] })
console.log(`${report.errorCount} errors, ${report.warningCount} warnings`)
```

##### on

```typescript
function on(ids: string, severity: "error" | "warn"): Record<string, RuleSetting>
```

Rules turned on at one severity.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `ids` | `string` | Rule ids separated by spaces or line breaks |
| `severity` | `"error" \| "warn"` | The severity every rule gets (default: `'error'`) |

**Returns** `Record<string, RuleSetting>`: The rules by id

**Example: Turn several rules on in one block**

```ts
import { on } from '@hyperfrontend/linter'

export default [
  { files: ['src/**'], rules: on('no-var prefer-const typescript/no-namespace') },
  { files: ['src/**'], rules: on('no-todo-comments', 'warn') },
]
```

##### run

```typescript
function run(argv: unknown, environment: RunEnvironment): Promise<number>
```

Runs the command.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `argv` | `unknown` | The arguments after the command |
| `environment` | `RunEnvironment` | The working directory and streams |

**Returns** `Promise<number>`: The exit code

**Example: Run the command inside another program**

```ts
import { run } from '@hyperfrontend/linter'

process.exitCode = await run(['src', '--max-warnings', '0'], {
  cwd: process.cwd(),
  readStdin: () => '',
  write: (text) => process.stdout.write(text),
  writeError: (text) => process.stderr.write(text),
})
```

#### Interfaces

##### ConfigBlock

One configuration block.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly files?` | `unknown` | Globs, relative to the configuration file's directory, of the files the block applies to; absent means every file the other blocks select. |
| `readonly ignores?` | `unknown` | Globs of files the block does not apply to; in a block without `files`, globs no block applies to. |
| `readonly rules?` | `Readonly<Record<string, RuleSetting>>` | Rules by id. |
| `readonly settings?` | `Readonly<Record<string, unknown>>` | Settings every rule of the block's files may read. |
| `readonly language?` | `Partial<LanguageSettings>` | How the code files the block selects are read; what is left out keeps the earlier blocks' value. |
| `readonly families?` | `Readonly<Record<string, RuleFamily>>` | Rules beyond the packaged ones, by family prefix; a family is registered for the whole configuration, whatever the block's `files`. |

##### FileResult

What linting one file produced.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly filePath` | `string` | The absolute path. |
| `readonly messages` | `unknown` | The problems left after fixes and suppressions, in position order. |
| `readonly output` | `string` | The text after fixes, or null when nothing changed. |

##### Fix

A replacement of one span of the text.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character replaced. |
| `readonly end` | `number` | The offset after the last character replaced. |
| `readonly text` | `string` | The replacement. |

##### JsonArrayNode

A bracket-delimited list of values.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |
| `readonly kind` | `"array"` | Marks the node as an array. |
| `readonly elements` | `unknown` | The elements in source order. |

##### JsonDocument

The node that holds a document's root value.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `"document"` | Marks the node as the document. |
| `readonly root` | `JsonNode` | The root value. |
| `readonly start` | `number` | The offset of the document's first character. |
| `readonly end` | `number` | The offset after its last character. |

##### JsonLiteralNode

A string, number, boolean or null.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |
| `readonly kind` | `"string" \| "number" \| "boolean" \| "null"` | Which literal it is. |
| `readonly value` | `string \| number \| boolean` | The parsed value. |
| `readonly raw` | `string` | The text as written, quotes and escapes included. |

##### JsonObjectNode

A brace-delimited list of properties.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |
| `readonly kind` | `"object"` | Marks the node as an object. |
| `readonly properties` | `unknown` | The properties in source order. |

##### JsonPropertyNode

One `"key": value` pair of an object.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |
| `readonly kind` | `"property"` | Marks the node as a property. |
| `readonly key` | `JsonLiteralNode` | The key, always a string literal. |
| `readonly value` | `JsonNode` | The value. |

##### JsonSpan

A range of the parsed text: the offset of its first character and the offset after its last.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |

##### LanguageSettings

How a code file's text is read: its module kind, the names it sees as globals and the JSX pragma.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly sourceType` | `"module" \| "script" \| "commonjs"` | `module` for an ES module, `script` for a classic script, `commonjs` for a CommonJS module. |
| `readonly globals` | `Readonly<Record<string, "readonly" \| "writable">>` | The global names beyond the language's own, each writable or read-only. |
| `readonly jsxPragma` | `string` | The identifier every JSX element references, or null when JSX references none. |
| `readonly jsxFragmentName` | `string` | The identifier a JSX fragment references in place of the pragma, or null for the pragma. |

##### LineMap

The line structure of one text.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly starts` | `unknown` | The offset of each line's first character; the first entry is 0. |
| `readonly text` | `string` | The text the map was built from. |

##### LintReport

What a lint produced.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly results` | `unknown` | One result per file, in the order the files were linted. |
| `readonly errorCount` | `number` | The errors across all results. |
| `readonly warningCount` | `number` | The warnings across all results. |

##### LintRequest

What to lint and how.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly cwd` | `string` | The working directory: the project root the files are linted under, where the configuration search starts and what relative paths are resolved against. |
| `readonly patterns?` | `unknown` | Files, directories or globs to lint, relative to `cwd`; absent or empty, `cwd` itself. Ignored when `text` is given. |
| `readonly config?` | `string` | A configuration module to load, relative to `cwd`, in place of the nearest `lint.config.ts`; its globs are then relative to `cwd`. |
| `readonly fix?` | `boolean` | Whether to apply fixes; each result's `output` then holds the fixed text, or null when nothing changed. Nothing is written. |
| `readonly text?` | `LintText` | A text to lint in place of files. |
| `readonly errorOnUnmatchedPattern?` | `boolean` | Whether a pattern that names no file is an error; true when absent. |

##### LintText

A text to lint in place of files, as `--stdin` reads it.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly path` | `string` | The path the text is linted as, relative to the working directory or absolute. |
| `readonly text` | `string` | The text. |

##### Location

A position a rule names outright, for a language whose reports do not follow the offsets.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly line` | `number` | The line, as printed. |
| `readonly column` | `number` | The column, as printed. |
| `readonly endLine?` | `number` | The end line, or absent for a point. |
| `readonly endColumn?` | `number` | The end column, or absent for a point. |

##### MarkdownNode

A node of the tree.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `MarkdownKind` | The kind, which selects listeners. |
| `readonly position` | `MarkdownPosition` | Where the node sits. |
| `readonly children?` | `unknown` | The children, in source order; absent on leaves. |
| `readonly value?` | `string` | The text of a text, inline code, code or HTML node. |
| `readonly depth?` | `number` | The level of a heading, 1 to 6. |
| `readonly lang?` | `string` | The language a fenced code block declares; absent when the fence names none. |
| `readonly meta?` | `string` | The rest of a fence's info string after the language. |
| `readonly url?` | `string` | The target of a link, image or definition. |
| `readonly title?` | `string` | The title of a link, image or definition. |
| `readonly alt?` | `string` | The alternative text of an image. |
| `readonly ordered?` | `boolean` | Whether a list is numbered. |
| `readonly start?` | `number` | The first number of an ordered list. |
| `readonly checked?` | `boolean` | The state of a task list item's checkbox. |
| `readonly identifier?` | `string` | The normalised label of a reference, definition or footnote. |
| `readonly align?` | `unknown` | The alignment of each table column. |

##### MarkdownPoint

A point in the source.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly line` | `number` | The 1-based line. |
| `readonly column` | `number` | The 1-based column. |
| `readonly offset` | `number` | The 0-based offset. |

##### MarkdownPosition

Where a node sits.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `MarkdownPoint` | The first character. |
| `readonly end` | `MarkdownPoint` | After the last character. |

##### Message

A problem as the linter answers it.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly ruleId` | `string` | The rule id, or null for a fatal problem such as a parse error. |
| `readonly severity` | `1 \| 2` | 1 for a warning, 2 for an error. |
| `readonly message` | `string` | The text shown to the user. |
| `readonly line` | `number` | The 1-based start line, or null for a problem about the file as a whole. |
| `readonly column` | `number` | The 1-based start column, or null for a problem about the file as a whole. |
| `readonly endLine` | `number` | The 1-based end line, or null when the problem has no range. |
| `readonly endColumn` | `number` | The 1-based end column, or null when the problem has no range. |
| `readonly fatal` | `boolean` | True when the problem stopped linting. |
| `readonly fix` | `Fix` | The fix offered, or null. |
| `readonly suggestions` | `unknown` | The suggestions offered. |

##### Node

A node of the tree.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `Kind` | The kind, which selects listeners. |
| `readonly start` | `number` | The offset of the first character. |
| `end` | `number` | The offset after the last character. |
| `parent` | `Node` | The node this one sits in, null for the program. |
| `body?` | `Node \| unknown` | A program's statements, a block's statements, a class body's members, a function's body, a loop's body, a labelled statement's body, a module block's statements. |
| `sourceType?` | `"module" \| "script"` | Whether a program is a module or a script. |
| `tokens?` | `unknown` | A program's tokens, in source order. |
| `expression?` | `boolean \| Node` | The expression of an expression statement, a decorator, a spread, an assertion, a chain, a container or an export assignment. |
| `directive?` | `string` | The directive text of a directive prologue statement. |
| `object?` | `Node` | The object of a with statement or a member expression. |
| `argument?` | `Node` | The argument of a return, throw, spread, rest, unary, update, yield, await or import type. |
| `label?` | `Node` | The label of a labelled, break or continue statement, or a named tuple member. |
| `test?` | `Node` | The test of an if, loop, switch case or conditional expression. |
| `consequent?` | `Node \| unknown` | The consequent of an if or conditional, or a switch case's statements. |
| `alternate?` | `Node` | The alternate of an if or conditional. |
| `discriminant?` | `Node` | The discriminant of a switch. |
| `cases?` | `unknown` | The cases of a switch. |
| `block?` | `Node` | The block of a try. |
| `handler?` | `Node` | The handler of a try. |
| `finalizer?` | `Node` | The finaliser of a try. |
| `param?` | `Node` | The parameter of a catch clause, null when it binds nothing. |
| `init?` | `Node` | The initialiser of a for statement, a declarator or an enum member. |
| `update?` | `Node` | The update of a for statement. |
| `left?` | `Node` | The left side of a for-in, for-of, binary, logical, assignment, assignment pattern or qualified name. |
| `right?` | `Node` | The right side of a for-in, for-of, binary, logical, assignment, assignment pattern or qualified name. |
| `await?` | `boolean` | Whether a for-of awaits, or a function awaits. |
| `id?` | `Node` | The identifier of a declaration, declarator, enum, module, interface, type alias or import equals. |
| `params?` | `unknown` | The parameters of a function, signature, arrow or type parameter list. |
| `parameters?` | `unknown` | The parameters of an index signature. |
| `generator?` | `boolean` | Whether a function is a generator. |
| `async?` | `boolean` | Whether a function is async. |
| `typeParameters?` | `Node` | The type parameters of a declaration or function. |
| `returnType?` | `Node` | The return type of a function or signature. |
| `declare?` | `boolean` | Whether a declaration is ambient. |
| `declarations?` | `unknown` | The declarators of a variable declaration. |
| `variableKind?` | `string` | Which kind of variable declaration: `var`, `let`, `const`, `using` or `await using`. |
| `definite?` | `boolean` | Whether a declarator or property is definitely assigned. |
| `superClass?` | `Node` | The superclass of a class. |
| `superTypeArguments?` | `Node` | The type arguments passed to the superclass. |
| `implements?` | `unknown` | The interfaces a class implements. |
| `abstract?` | `boolean` | Whether a class or member is abstract. |
| `decorators?` | `unknown` | The decorators of a class, member or parameter. |
| `key?` | `Node` | The key of a member, property, signature, enum member, mapped type or import attribute. |
| `value?` | `string \| number \| bigint \| boolean \| RegExp \| RegexValue \| TemplateValue \| Node` | The value of a member, property, literal, template element or import attribute. |
| `computed?` | `boolean` | Whether a key is computed. |
| `static?` | `boolean` | Whether a member is static. |
| `methodKind?` | `string` | Which kind of method: `constructor`, `method`, `get` or `set`. |
| `propertyKind?` | `string` | Which kind of property: `init`, `get` or `set`. |
| `accessibility?` | `string` | The accessibility of a member: `public`, `protected` or `private`. |
| `override?` | `boolean` | Whether a member overrides. |
| `optional?` | `string \| boolean` | Whether a member, parameter, tuple member or type is optional. |
| `readonly?` | `string \| boolean` | Whether a member, parameter, signature or type is read-only. |
| `typeAnnotation?` | `Node` | The type annotation of an identifier, pattern, member, signature or mapped type. |
| `specifiers?` | `unknown` | The specifiers of an import or export. |
| `source?` | `Node` | The source of an import, export or dynamic import. |
| `importKind?` | `"type" \| "value"` | Whether an import is of types or values. |
| `exportKind?` | `"type" \| "value"` | Whether an export is of types or values. |
| `attributes?` | `unknown` | The import attributes. |
| `imported?` | `Node` | The name imported by a specifier. |
| `local?` | `Node` | The local name of a specifier. |
| `exported?` | `Node` | The name exported by a specifier or namespace export. |
| `declaration?` | `Node` | The declaration an export carries. |
| `moduleReference?` | `Node` | The module reference of an import equals. |
| `name?` | `string \| Node` | The name of an identifier, JSX identifier or private identifier. |
| `raw?` | `string` | The raw text of a literal or JSX text. |
| `regex?` | `RegexValue` | The pattern and flags of a regular expression literal. |
| `bigint?` | `string` | The digits of a bigint literal. |
| `quasis?` | `unknown` | The quasis of a template literal or template literal type. |
| `expressions?` | `unknown` | The expressions of a template literal or sequence. |
| `tail?` | `boolean` | Whether a template element is the last. |
| `tag?` | `Node` | The tag of a tagged template. |
| `quasi?` | `Node` | The quasi of a tagged template. |
| `elements?` | `unknown` | The elements of an array expression or pattern, null for holes. |
| `properties?` | `unknown` | The properties of an object expression or pattern. |
| `shorthand?` | `boolean` | Whether a property is shorthand. |
| `method?` | `boolean` | Whether a property is a method. |
| `operator?` | `string` | The operator of a unary, update, binary, logical, assignment or type operator. |
| `prefix?` | `boolean` | Whether an update is prefix. |
| `callee?` | `Node` | The callee of a call or new. |
| `arguments?` | `unknown` | The arguments of a call or new. |
| `typeArguments?` | `Node` | The type arguments of a call, new, tagged template, reference, heritage, instantiation or JSX element. |
| `property?` | `Node` | The property of a member expression or meta property. |
| `delegate?` | `boolean` | Whether a yield delegates. |
| `options?` | `Node` | The options of a dynamic import. |
| `meta?` | `Node` | The meta of a meta property. |
| `parameter?` | `Node` | The parameter a parameter property wraps. |
| `openingElement?` | `Node` | The opening element or fragment. |
| `closingElement?` | `Node` | The closing element, null when self-closing. |
| `openingFragment?` | `Node` | The opening fragment. |
| `closingFragment?` | `Node` | The closing fragment. |
| `children?` | `unknown` | The children of a JSX element or fragment. |
| `selfClosing?` | `boolean` | Whether an opening element is self-closing. |
| `namespace?` | `Node` | The namespace of a namespaced name. |
| `constraint?` | `Node` | The constraint of a type parameter or mapped type. |
| `default?` | `Node` | The default of a type parameter. |
| `in?` | `boolean` | Whether a type parameter is `in`. |
| `out?` | `boolean` | Whether a type parameter is `out`. |
| `const?` | `boolean` | Whether a type parameter is `const`, or an enum is const. |
| `extends?` | `unknown` | The interfaces an interface extends. |
| `members?` | `unknown` | The members of an enum body, type literal or interface body. |
| `moduleKind?` | `string` | Which kind of module declaration: `module`, `namespace` or `global`. |
| `global?` | `boolean` | Whether a module declaration is global. |
| `typeName?` | `Node` | The name of a type reference. |
| `types?` | `unknown` | The types of a union, intersection or template literal type. |
| `elementType?` | `Node` | The element type of an array type or named tuple member. |
| `elementTypes?` | `unknown` | The element types of a tuple. |
| `checkType?` | `Node` | The check type of a conditional type. |
| `extendsType?` | `Node` | The extends type of a conditional type. |
| `trueType?` | `Node` | The true type of a conditional type. |
| `falseType?` | `Node` | The false type of a conditional type. |
| `typeParameter?` | `Node` | The type parameter of an infer type. |
| `nameType?` | `Node` | The name type of a mapped type. |
| `objectType?` | `Node` | The object type of an indexed access. |
| `indexType?` | `Node` | The index type of an indexed access. |
| `exprName?` | `Node` | The name queried by a type query. |
| `qualifier?` | `Node` | The qualifier of an import type. |
| `literal?` | `Node` | The literal of a literal type. |
| `parameterName?` | `Node` | The parameter name of a type predicate. |
| `asserts?` | `boolean` | Whether a type predicate asserts. |

##### Position

A position in a text as reports name it.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly line` | `number` | The 1-based line. |
| `readonly column` | `number` | The 1-based column. |

##### Problem

What a rule reports, with offsets.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset where the problem starts; what the directives and the ordering go by. |
| `readonly end?` | `number` | The offset where it ends; absent when the rule names a point rather than a range. |
| `readonly at?` | `Location` | The position printed instead of the one the offsets give, when the rule names it. |
| `readonly message` | `string` | The text shown to the user. |
| `readonly fix?` | `Fix` | A fix, when the rule offers one. |
| `readonly suggestions?` | `unknown` | Suggestions, when the rule offers any; they are reported, not applied. |

##### RegexValue

A regular expression literal's value.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly pattern` | `string` | The pattern between the slashes. |
| `readonly flags` | `string` | The flags after the closing slash. |

##### RuleContext

What a rule receives.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly file` | `RuleFile` | The file. |
| `readonly tree` | `Node` | The tree the language parsed. |
| `readonly options` | `Options` | The rule's options as configured, or undefined. |
| `readonly settings` | `Readonly<Record<string, unknown>>` | The settings of the blocks that matched the file. |
| `readonly language` | `LanguageSettings` | How the file's text is read: module kind, globals and JSX pragma. |
| `readonly report` | `(problem: Problem) => void` | Reports a problem. |

##### RuleFile

The file a rule is running on.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly path` | `string` | The absolute path. |
| `readonly root` | `string` | The project root the file is linted under, absolute. |
| `readonly relativePath` | `string` | The path relative to that root, POSIX. |
| `readonly text` | `string` | The text as linted in this pass. |
| `readonly lines` | `LineMap` | The text's line map. |
| `readonly comments` | `unknown` | The comments the language found. |

##### RuleModule

A rule module's default export.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly id` | `string` | The id configurations name, such as `no-enum`. |
| `readonly language` | `Language` | The language the rule reads. |
| `readonly fixable` | `boolean` | Whether the rule offers fixes. |

###### create

```typescript
create(context: RuleContext<Options, Node>): Listeners<Node>
```

Builds the listeners for one file.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `context` | `RuleContext<Options, Node>` | The file, its tree, the rule's options and the report function |

**Returns** `Listeners<Node>`: The listeners by node kind

##### RuleModuleExports

A rule module as `import()` returns it.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly default` | `RuleModule` | The rule. |

##### RunEnvironment

Where the command runs and how it talks to the world.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly cwd` | `string` | The working directory, which is the project root. |
| `readonly readStdin` | `() => string` | Standard input, read when `--stdin` is given. |
| `readonly write` | `(text: string) => void` | Writes to standard output. |
| `readonly writeError` | `(text: string) => void` | Writes to standard error. |

##### SourceComment

A comment as a language hands it to the engine.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the comment's first character. |
| `readonly end` | `number` | The offset after its last character. |
| `readonly text` | `string` | The comment's text without its delimiters. |

##### SourceToken

A token the parser consumed.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly category` | `string` | `word`, `number`, `string`, `template`, `regex`, `jsxtext` or `punct`. |
| `readonly start` | `number` | The offset of its first character. |
| `readonly end` | `number` | The offset after its last character. |

##### Suggestion

A fix the user may choose to apply.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly message` | `string` | What applying it does, in words. |
| `readonly fix` | `Fix` | The replacement. |

##### TemplateValue

The value of a template element.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly raw` | `string` | The text as written. |
| `readonly cooked` | `string` | The text with escapes decoded, or null when an escape is invalid. |

##### TextDocument

The one node of a text document.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `"document"` | Marks the node as the document. |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |

##### TreeNode

The least a tree node carries.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `string` | The node's kind, which selects listeners. |

#### Types

##### JsonNode

Any value.

```typescript
type JsonNode = JsonObjectNode | JsonArrayNode | JsonLiteralNode
```

##### JsonTreeNode

Any node of a JSON tree.

```typescript
type JsonTreeNode = JsonDocument | JsonNode | JsonPropertyNode
```

##### Kind

The node kinds.

```typescript
type Kind = "Program" | "ExpressionStatement" | "BlockStatement" | "EmptyStatement" | "DebuggerStatement" | "WithStatement" | "ReturnStatement" | "LabeledStatement" | "BreakStatement" | "ContinueStatement" | "IfStatement" | "SwitchStatement" | "SwitchCase" | "ThrowStatement" | "TryStatement" | "CatchClause" | "WhileStatement" | "DoWhileStatement" | "ForStatement" | "ForInStatement" | "ForOfStatement" | "FunctionDeclaration" | "TSDeclareFunction" | "VariableDeclaration" | "VariableDeclarator" | "ClassDeclaration" | "ClassExpression" | "ClassBody" | "MethodDefinition" | "PropertyDefinition" | "TSAbstractMethodDefinition" | "TSAbstractPropertyDefinition" | "AccessorProperty" | "StaticBlock" | "TSIndexSignature" | "Decorator" | "ImportDeclaration" | "ImportSpecifier" | "ImportDefaultSpecifier" | "ImportNamespaceSpecifier" | "ImportAttribute" | "ExportNamedDeclaration" | "ExportSpecifier" | "ExportDefaultDeclaration" | "ExportAllDeclaration" | "TSExportAssignment" | "TSImportEqualsDeclaration" | "TSExternalModuleReference" | "TSNamespaceExportDeclaration" | "Identifier" | "PrivateIdentifier" | "Literal" | "TemplateLiteral" | "TemplateElement" | "TaggedTemplateExpression" | "ThisExpression" | "Super" | "ArrayExpression" | "ObjectExpression" | "Property" | "FunctionExpression" | "TSEmptyBodyFunctionExpression" | "ArrowFunctionExpression" | "UnaryExpression" | "UpdateExpression" | "BinaryExpression" | "LogicalExpression" | "AssignmentExpression" | "ConditionalExpression" | "CallExpression" | "NewExpression" | "MemberExpression" | "ChainExpression" | "SequenceExpression" | "YieldExpression" | "AwaitExpression" | "ImportExpression" | "MetaProperty" | "SpreadElement" | "ArrayPattern" | "ObjectPattern" | "RestElement" | "AssignmentPattern" | "TSAsExpression" | "TSSatisfiesExpression" | "TSNonNullExpression" | "TSTypeAssertion" | "TSInstantiationExpression" | "TSParameterProperty" | "JSXElement" | "JSXFragment" | "JSXOpeningElement" | "JSXClosingElement" | "JSXOpeningFragment" | "JSXClosingFragment" | "JSXAttribute" | "JSXSpreadAttribute" | "JSXIdentifier" | "JSXNamespacedName" | "JSXMemberExpression" | "JSXText" | "JSXExpressionContainer" | "JSXEmptyExpression" | "JSXSpreadChild" | "TSTypeAnnotation" | "TSTypeParameterDeclaration" | "TSTypeParameter" | "TSTypeParameterInstantiation" | "TSInterfaceDeclaration" | "TSInterfaceBody" | "TSInterfaceHeritage" | "TSClassImplements" | "TSTypeAliasDeclaration" | "TSEnumDeclaration" | "TSEnumBody" | "TSEnumMember" | "TSModuleDeclaration" | "TSModuleBlock" | "TSPropertySignature" | "TSMethodSignature" | "TSCallSignatureDeclaration" | "TSConstructSignatureDeclaration" | "TSTypeLiteral" | "TSTypeReference" | "TSQualifiedName" | "TSUnionType" | "TSIntersectionType" | "TSArrayType" | "TSTupleType" | "TSNamedTupleMember" | "TSOptionalType" | "TSRestType" | "TSFunctionType" | "TSConstructorType" | "TSConditionalType" | "TSInferType" | "TSMappedType" | "TSIndexedAccessType" | "TSTypeOperator" | "TSTypeQuery" | "TSImportType" | "TSLiteralType" | "TSTemplateLiteralType" | "TSTypePredicate" | "TSThisType" | "TSAnyKeyword" | "TSUnknownKeyword" | "TSNeverKeyword" | "TSVoidKeyword" | "TSUndefinedKeyword" | "TSNullKeyword" | "TSStringKeyword" | "TSNumberKeyword" | "TSBooleanKeyword" | "TSBigIntKeyword" | "TSSymbolKeyword" | "TSObjectKeyword" | "TSIntrinsicKeyword"
```

##### Language

The languages a rule can read.

```typescript
type Language = "code" | "json" | "markdown" | "text"
```

##### Listener

What a listener receives.

```typescript
type Listener = (node: Node, parents: unknown) => void
```

##### Listeners

Listeners by kind, `kind:exit` for the way out.

```typescript
type Listeners = Readonly<Record<string, Listener<Node>>>
```

##### MarkdownAlign

How a table column is aligned.

```typescript
type MarkdownAlign = "left" | "right" | "center" | null
```

##### MarkdownKind

The node kinds, named as mdast names them.

```typescript
type MarkdownKind = "root" | "heading" | "paragraph" | "text" | "inlineCode" | "code" | "html" | "thematicBreak" | "blockquote" | "list" | "listItem" | "table" | "tableRow" | "tableCell" | "link" | "linkReference" | "image" | "imageReference" | "definition" | "footnoteDefinition" | "footnoteReference" | "emphasis" | "strong" | "delete" | "break"
```

##### RuleFamily

A family's rules by name; a rule's id is the family's prefix, a slash and the name.

```typescript
type RuleFamily = Readonly<Record<string, RuleSource>>
```

##### RuleSetting

How a rule is set: off, or a severity with optional options.

```typescript
type RuleSetting = "off" | "warn" | "error" | unknown
```

##### RuleSource

A rule, or a function that imports the module whose default export is the rule.

```typescript
type RuleSource = RuleModule | () => Promise<RuleModule | RuleModuleExports>
```

#### Variables

##### BROWSER_AND_NODE_GLOBALS

The globals, each read-only.

```typescript
const BROWSER_AND_NODE_GLOBALS: indexedAccess
```

**Value** `...`

##### CODE

The code files every code rule reads.

```typescript
const CODE: string[]
```

**Value** `...`

##### CODE_RULES

The core and typescript-eslint rules the recommended sets and the workspace's adjustments leave on.

```typescript
const CODE_RULES: Readonly<Record<string, RuleSetting>>
```

**Value** `...`

##### JAVASCRIPT

The JavaScript files, which see the browser's and Node's globals.

```typescript
const JAVASCRIPT: string[]
```

**Value** `...`

##### JSDOC_RULES

The JSDoc rules the recommended set turns on, as warnings.

```typescript
const JSDOC_RULES: Readonly<Record<string, RuleSetting>>
```

**Value** `...`

##### REACT_RULES

The React and hooks rules the recommended sets turn on, less the two a modern JSX transform and TypeScript make moot.

```typescript
const REACT_RULES: Readonly<Record<string, RuleSetting>>
```

**Value** `...`

##### RULE_IDS

The ids of the packaged rules, in alphabetical order.

```typescript
const RULE_IDS: unknown
```

**Value** `...`

##### SCRIPT

The files the JSDoc and boundary rules read.

```typescript
const SCRIPT: string[]
```

**Value** `...`

##### TYPESCRIPT

The TypeScript sources the workspace rules read.

```typescript
const TYPESCRIPT: string[]
```

**Value** `...`

### @hyperfrontend/linter/helpers

What rules ask beyond the tree: tokens and comments around a node, children, scopes and
variables, JSON properties, positions, globs, merged fixes, and parsing another file.

#### Functions

##### childrenOf

```typescript
function childrenOf(node: Node): Node[]
```

The children of a node, in source order.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `node` | `Node` | Any node |

**Returns** `Node[]`: The child nodes

**Example: List an if statement's children**

```ts
const { tree } = parseCode('if (ready) start()', '.ts')
childrenOf(tree.body[0]).map((child) => child.kind)
// => ['Identifier', 'ExpressionStatement'] (the null alternate is skipped)
```

##### commentsBetween

```typescript
function commentsBetween(file: RuleFile, start: number, end: number): SourceComment[]
```

The comments starting at or after one offset and ending at or before another.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `file` | `RuleFile` | The file linted |
| `start` | `number` | The first offset |
| `end` | `number` | The last offset |

**Returns** `SourceComment[]`: The comments, in source order

**Example: Find the comments inside a range**

```ts
// file.text: 'const total = price * 2 // doubled'
commentsBetween(file, 0, file.text.length)
// => [{ start: 24, end: 34, text: ' doubled', ... }]
```

##### compileGlob

```typescript
function compileGlob(pattern: string): Glob
```

Compiles a pattern.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `pattern` | `string` | The text as a configuration wrote it |

**Returns** `Glob`: The same text with a matcher beside it

**Example: Match alternatives, everything below, or a whole directory**

```ts
const entries = compileGlob('src/index.{ts,tsx}')
entries.test('src/index.tsx') // => true
entries.test('src/index.js') // => false

compileGlob('src/**').test('src/ui/button.tsx') // => true

const dist = compileGlob('dist/')
dist.test('dist/index.js') // => true
dist.test('distance.ts') // => false
```

##### findVariable

```typescript
function findVariable(scope: Scope, name: string): Variable
```

The variable a name resolves to from a scope, looking outward.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `scope` | `Scope` | The scope looked from |
| `name` | `string` | The name looked up |

**Returns** `Variable`: The variable, or null when no scope in reach declares it

**Example: Resolve a name outward from a function's scope**

```ts
const count = findVariable(scopeOf(analysis, step), 'count')
count.scope.kind // => 'module'
count.references.map((reference) => reference.direction)
// => ['write', 'read-write']
findVariable(scopeOf(analysis, step), 'missing') // => null
```

##### indentationAt

```typescript
function indentationAt(text: string, offset: number): string
```

The whitespace that precedes a node on its line.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | The document |
| `offset` | `number` | The node's start |

**Returns** `string`: The spaces and tabs between the line start and the node

**Example: Match a property's indentation in a fix**

```ts
const text = '{\n  "files": ["dist"]\n}'
indentationAt(text, 4) // => '  '
```

##### isGlobalReference

```typescript
function isGlobalReference(analysis: ScopeAnalysis, identifier: Node): boolean
```

Whether an identifier refers to a global: a name the language or the configuration declares,
which no declaration of the program shadows, or a name nothing declares at all.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `analysis` | `ScopeAnalysis` | The program's scope analysis |
| `identifier` | `Node` | The identifier tested |

**Returns** `boolean`: Whether the reference resolves to no declaration of the program

**Example: Tell the language's undefined from a parameter named undefined**

```ts
// program: limit + undefined
//          function read(undefined) { return undefined }
isGlobalReference(analysis, undefinedInSum) // => true
isGlobalReference(analysis, undefinedInRead) // => false (the parameter)
```

##### isParenthesised

```typescript
function isParenthesised(program: Node, file: RuleFile, node: Node): boolean
```

Whether a node sits directly between an opening and a closing parenthesis.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `program` | `Node` | The program whose tokens are searched |
| `file` | `RuleFile` | The file linted |
| `node` | `Node` | The node looked around |

**Returns** `boolean`: Whether the tokens around it are parentheses

**Example: Tell a wrapped operand from its expression**

```ts
// file.text: 'const total = (price) * 2'
isParenthesised(program, file, price) // => true
isParenthesised(program, file, init) // => false (the whole BinaryExpression)
```

##### isString

```typescript
function isString(node: JsonNode): unknown
```

Whether a node is a string literal.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `node` | `JsonNode` | Any value node |

**Returns** `unknown`: True for a string

**Example: Narrow a property value to a string**

```ts
// { "name": "@acme/ui", "private": true }
isString(propertyOf(manifest, 'name').value) // => true
isString(propertyOf(manifest, 'private').value) // => false
```

##### lineText

```typescript
function lineText(map: LineMap, line: number): string
```

The text of a line.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `map` | `LineMap` | The table `lineMap` built for the file |
| `line` | `number` | Which line, counted from 1 |

**Returns** `string`: That line's characters without the break that ends it

**Example: Read a line without its break**

```ts
const map = lineMap('first\r\nsecond\n')
lineText(map, 1) // => 'first'
lineText(map, 3) // => '' (the empty line after the last break)
```

##### matchesAny

```typescript
function matchesAny(globs: unknown, path: string): boolean
```

Whether a path matches any of the patterns.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `globs` | `unknown` | Compiled patterns |
| `path` | `string` | A POSIX path relative to the patterns' base |

**Returns** `boolean`: True when one matches

**Example: Check a path against ignore patterns**

```ts
const ignores = [compileGlob('dist/'), compileGlob('*.config.ts')]
matchesAny(ignores, 'jest.config.ts') // => true
matchesAny(ignores, 'src/app.ts') // => false
```

##### mergeEdits

```typescript
function mergeEdits(text: string, edits: unknown): Fix
```

Folds edits that do not overlap into one replacement.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | The text the edits apply to |
| `edits` | `unknown` | The edits, in any order; an insertion has `start` equal to `end` |

**Returns** `Fix`: One replacement covering them all

**Example: Fold a keyword change and an annotation removal into one fix**

```ts
const text = 'let total: any = 0'
mergeEdits(text, [{ start: 9, end: 14, text: '' }, { start: 0, end: 3, text: 'const' }])
// => { start: 0, end: 14, text: 'const total' }
```

##### parseCode

```typescript
function parseCode(text: string, extension: string, sourceType: "module" | "script" | "commonjs", declaration: boolean): ParsedCode
```

Parses a code file.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | The source text |
| `extension` | `string` | The file's extension with its dot |
| `sourceType` | `"module" \| "script" \| "commonjs"` | Whether the text is a module, a script or a CommonJS module; a script's strings may hold legacy escapes (default: `'module'`) |
| `declaration` | `boolean` | Whether the text is a declaration file, where declarations need no initialiser or body (default: `false`) |

**Returns** `ParsedCode`: The tree, comments and tokens, or the error

**Example: Parse a file, or read why it did not parse**

```ts
const parsed = parseCode('const total = price * 2 // doubled', '.ts')
parsed.tree.kind // => 'Program'
parsed.comments[0].text // => ' doubled'

parseCode('const = 1', '.ts').error
// => { message: 'Identifier expected.', offset: 6 }
```

##### positionAt

```typescript
function positionAt(map: LineMap, offset: number): Position
```

The line and column of an offset.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `map` | `LineMap` | The text's line map |
| `offset` | `number` | An offset into the text, up to and including its length |

**Returns** `Position`: The 1-based position

**Example: Turn an offset into a 1-based position**

```ts
const map = lineMap('const a = 1\nconst b = 2')
positionAt(map, 0) // => { line: 1, column: 1 }
positionAt(map, 18) // => { line: 2, column: 7 }
```

##### propertyOf

```typescript
function propertyOf(object: JsonObjectNode, name: string): JsonPropertyNode
```

A property of an object by name.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `object` | `JsonObjectNode` | The node whose properties are searched |
| `name` | `string` | The key to look for |

**Returns** `JsonPropertyNode`: The first property with that name, or null

**Example: Look up a field of package.json**

```ts
// { "name": "@acme/ui", "files": ["dist"] }
propertyOf(manifest, 'files')?.value.kind // => 'array'
propertyOf(manifest, 'version') // => null
```

##### referenceOf

```typescript
function referenceOf(analysis: ScopeAnalysis, identifier: Node): Reference
```

The reference an identifier makes, found through the analysis of its program.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `analysis` | `ScopeAnalysis` | The program's scope analysis |
| `identifier` | `Node` | The identifier looked up |

**Returns** `Reference`: The reference, or null when the identifier makes none

**Example: Follow a use of a name back to its variable**

```ts
// program: const limit = 10 / limit + 1 / function check() {}
referenceOf(analysis, limitInSum)?.resolved?.name // => 'limit'
referenceOf(analysis, checkDeclaration.id) // => null (a declaration, not a use)
```

##### rootObject

```typescript
function rootObject(node: JsonTreeNode): JsonObjectNode
```

The root object of a document.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `node` | `JsonTreeNode` | The node a `document` listener received |

**Returns** `JsonObjectNode`: The root when it is an object, or null

**Example: Start a rule from the document's root object**

```ts
document(node) {
  const manifest = rootObject(node)
  if (manifest === null) return // the document is an array or a literal
}
```

##### scopeAnalysisOf

```typescript
function scopeAnalysisOf(context: RuleContext<unknown, Node>): ScopeAnalysis
```

The scope analysis of the program a rule is linting, built on first request.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `context` | `RuleContext<unknown, Node>` | The rule's context |

**Returns** `ScopeAnalysis`: The analysis, the same object on every request for the program

**Example: Share one analysis across a rule's handlers**

```ts
const analysis = scopeAnalysisOf(context)
scopeAnalysisOf(context) === analysis // => true, built once per program
```

##### scopeOf

```typescript
function scopeOf(analysis: ScopeAnalysis, node: Node): Scope
```

The innermost scope a node sits in: for the program itself the global scope, for any other
node the innermost scope its nearest scope-opening ancestor opened, which for a named
function expression is the function's scope rather than its name's.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `analysis` | `ScopeAnalysis` | The program's analysis |
| `node` | `Node` | The node whose scope is wanted |

**Returns** `Scope`: The innermost scope enclosing the node

**Example: Find the scope an identifier sits in**

```ts
// program: 'let count = 0\nfunction bump(step) {\n  count += step\n}'
// step: the Identifier read inside bump
scopeOf(analysis, step).kind // => 'function'
scopeOf(analysis, program).kind // => 'global'
```

##### stringsUnder

```typescript
function stringsUnder(node: JsonNode): JsonStringNode[]
```

Every string literal under a node, in source order: the node itself, or the values nested in
its objects and arrays.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `node` | `JsonNode` | The node to search |

**Returns** `JsonStringNode[]`: The string nodes

**Example: Collect the strings of a nested value**

```ts
// "files": ["dist", { "glob": "src" }, 3]
stringsUnder(files.value).map((node) => node.value) // => ['dist', 'src']
```

##### tokenAfter

```typescript
function tokenAfter(program: Node, offset: number): SourceToken
```

The first token starting at or after an offset.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `program` | `Node` | The program whose tokens are searched |
| `offset` | `number` | The offset searched from |

**Returns** `SourceToken`: The token, or null when none starts there or later

**Example: Find the token after a node**

```ts
// file.text: 'const total = (price) * 2', price: its Identifier node
tokenText(file, tokenAfter(program, price.end)) // => ')'
tokenAfter(program, file.text.length) // => null
```

##### tokenBefore

```typescript
function tokenBefore(program: Node, offset: number): SourceToken
```

The last token ending at or before an offset.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `program` | `Node` | The program whose tokens are searched |
| `offset` | `number` | The offset searched back from |

**Returns** `SourceToken`: The token, or null when none ends before it

**Example: Find the token before a node**

```ts
// file.text: 'const total = (price) * 2', price: its Identifier node
const open = tokenBefore(program, price.start)
tokenText(file, open) // => '('
tokenBefore(program, 0) // => null
```

##### tokensBetween

```typescript
function tokensBetween(program: Node, start: number, end: number): SourceToken[]
```

The tokens starting at or after one offset and ending at or before another.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `program` | `Node` | The program whose tokens are searched |
| `start` | `number` | The first offset |
| `end` | `number` | The last offset |

**Returns** `SourceToken[]`: The tokens, in source order

**Example: List the tokens a node spans**

```ts
// file.text: 'const total = (price) * 2', init: the BinaryExpression
const tokens = tokensBetween(program, init.start, init.end)
tokens.map((token) => tokenText(file, token))
// => ['(', 'price', ')', '*', '2']
```

##### tokenText

```typescript
function tokenText(file: RuleFile, token: SourceToken): string
```

The text of a token.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `file` | `RuleFile` | The file linted |
| `token` | `SourceToken` | The token read |

**Returns** `string`: Its characters

**Example: Read a token's characters**

```ts
// file.text: 'const total = 2'
tokenText(file, program.tokens[1]) // => 'total'
```

##### wholeDocument

```typescript
function wholeDocument(file: RuleFile): JsonSpan
```

The span of the whole document, where a rule reports a fact about the file as a whole.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `file` | `RuleFile` | The file being linted |

**Returns** `JsonSpan`: A problem's start and end

**Example: Report a fact about the whole file**

```ts
context.report({ ...wholeDocument(context.file), message: 'Missing "license" field.' })
// wholeDocument(file) for an 86-character file => { start: 0, end: 86 }
```

#### Interfaces

##### Definition

One declaration of a variable.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `DefinitionKind` | What declared it. |
| `readonly name` | `Node` | The identifier declared, or the string literal naming an enum member. |
| `readonly node` | `Node` | The declaring node: the declarator, the function or class, the import specifier, the enum member, the module, the type. |
| `readonly parent` | `Node` | The statement the declaring node belongs to, when one exists: the variable or import declaration. |
| `readonly rest` | `boolean` | Whether a parameter is the rest parameter. |
| `readonly isType` | `boolean` | Whether the declaration is valid in a type position. |
| `readonly isValue` | `boolean` | Whether the declaration is valid in a value position. |

##### Glob

A compiled pattern.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly pattern` | `string` | The pattern as written. |
| `readonly test` | `(path: string) => boolean` | Whether a path matches. |

##### JsonStringNode

A string literal node.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly start` | `number` | The offset of the first character. |
| `readonly end` | `number` | The offset after the last character. |
| `readonly kind` | `"string"` | Marks the literal as a string. |
| `readonly value` | `string` | The decoded text. |
| `readonly raw` | `string` | The text as written, quotes and escapes included. |

##### ParsedCode

What parsing a text produced.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly tree` | `Node` | The program, or null when the text did not parse. |
| `readonly comments` | `unknown` | The comments found, in source order. |
| `readonly tokens` | `unknown` | The tokens consumed, in source order. |
| `readonly error` | `ParseError` | The error that stopped parsing, or null. |

##### ParseError

Why a text did not parse.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly message` | `string` | What was wrong. |
| `readonly offset` | `number` | The offset where it was found. |

##### Reference

One use of a name.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly identifier` | `Node` | The identifier, or JSX identifier, making the reference; a declaration's own identifier for a JSX pragma reference. |
| `readonly from` | `Scope` | The scope the reference was made from. |
| `resolved` | `Variable` | The variable it resolved to, or null. |
| `readonly direction` | `Direction` | Whether it reads, writes, or both. |
| `readonly kind` | `ReferenceKind` | Whether it stands in a value position, a type position, or both. |
| `readonly writeExpr` | `Node` | For a write: the expression assigned, when one exists. |
| `readonly init` | `boolean` | For a write: whether it is the declaration's own initialisation. |
| `get isTypeReference` | `boolean` | Whether the reference stands in a type position. |
| `get isValueReference` | `boolean` | Whether the reference stands in a value position. |

###### isRead

```typescript
isRead(): boolean
```

Whether the reference reads.

**Returns** `boolean`: True for a read or a read-write

###### isWrite

```typescript
isWrite(): boolean
```

Whether the reference writes.

**Returns** `boolean`: True for a write or a read-write

**Example: A read keeps no write expression**

```ts
const reference = new Reference(
  identifier, scope, 'read', 'value', initialiser, true
)
reference.writeExpr // => null
reference.init // => false
reference.isRead() // => true
```

##### Scope

One scope.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly kind` | `ScopeKind` | Which construct opened it. |
| `readonly block` | `Node` | The node that opened it. |
| `readonly upper` | `Scope` | The enclosing scope, or null for the global scope. |
| `readonly children` | `Scope[]` | The scopes nested in it, in source order. |
| `readonly variables` | `Variable[]` | The variables it declares, in declaration order. |
| `readonly set` | `Map<string, Variable>` | Its variables by name. |
| `readonly references` | `Reference[]` | The references made from it, in source order. |
| `readonly through` | `Reference[]` | The references that passed through it unresolved. |
| `readonly variableScope` | `Scope` | The nearest scope, this one included, that a `var` declaration lands in. |
| `isStrict` | `boolean` | Whether its code is strict. |
| `left` | `Reference[]` | The references made in it or passed up to it, still to resolve; null once closed. |

**Example: A block scope's var declarations land in the module scope**

```ts
const global = new Scope('global', program, null, false)
const module = new Scope('module', program, global, true)
const block = new Scope('block', statement, module, true)
global.children.length // => 1
block.variableScope === module // => true
```

##### ScopeAnalysis

The scopes of one program.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly global` | `Scope` | The global scope. |
| `readonly scopes` | `unknown` | Every scope, in the order opened. |
| `readonly scopesOf` | `(block: Node) => unknown` | The scopes a node opened, in the order opened; none for most nodes. |
| `readonly declaredVariables` | `(node: Node) => unknown` | The variables a node declares: a declaration's declarators, a function's name and parameters, an import's bindings. |

##### Variable

A name declared in a scope, or known to the global scope.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly name` | `string` | The name as declared. |
| `readonly scope` | `Scope` | The scope that declares it. |
| `readonly identifiers` | `Node[]` | The identifiers that declare it, in source order. |
| `readonly defs` | `Definition[]` | Its declarations. |
| `readonly references` | `Reference[]` | The references that resolved to it, in resolution order. |
| `writable` | `boolean` | For a global: whether it may be assigned; null for a declared variable. |
| `readonly globalKind` | `ReferenceKind` | For a global: the positions it is valid in; null for a declared variable. |
| `used` | `boolean` | Whether a rule marked it used. |
| `get isType` | `boolean` | Whether the variable is valid in a type position; a global with fixed positions keeps them whatever a script declares. |
| `get isValue` | `boolean` | Whether the variable is valid in a value position; a global with fixed positions keeps them whatever a script declares. |

**Example: A global's positions are fixed, a declared variable's follow its definitions**

```ts
const options = { kind: 'value', writable: false }
const window = new Variable('window', globalScope, options)
window.isType // => false
window.writable // => false

const total = new Variable('total', moduleScope, null)
total.writable // => null (not a global)
total.isType // => true until a definition says otherwise
```

#### Types

##### SourceType

How a text is read as a module or a script.

```typescript
type SourceType = indexedAccess
```

### @hyperfrontend/linter/testing

Specifying a rule: valid texts that must report nothing and invalid texts with the messages,
positions and fixed text they must produce, each case one `node:test` test.

#### Functions

##### testRule

```typescript
function testRule(rule: RuleModule, cases: RuleCases): void
```

Declares the tests of a rule.

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `rule` | `RuleModule` | The rule under test |
| `cases` | `RuleCases` | Its valid and invalid cases |

**Example: Specify a rule with one valid and one invalid case**

```ts
import { testRule } from '@hyperfrontend/linter/testing'
import rule from './no-debugger'

testRule(rule, {
  valid: [{ name: 'a plain call', path: '/project/a.ts', text: 'run()\n' }],
  invalid: [
    {
      name: 'a debugger statement',
      path: '/project/a.ts',
      text: 'debugger\n',
      errors: [{ message: "Unexpected 'debugger' statement.", line: 1, column: 1 }],
    },
  ],
})
```

#### Interfaces

##### ExpectedError

A message the rule must report.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly message` | `string` | The text shown to the user. |
| `readonly line` | `number` | The 1-based start line, or null for a problem about the file as a whole. |
| `readonly column` | `number` | The 1-based start column, or null for a problem about the file as a whole. |
| `readonly endLine?` | `number` | The 1-based end line, when checked; null for a problem without a range. |
| `readonly endColumn?` | `number` | The 1-based end column, when checked; null for a problem without a range. |

##### InvalidCase

A text the rule must report on.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly name` | `string` | The test's name. |
| `readonly path` | `string` | The absolute path the text is linted as; its directory is the project root unless `root` says otherwise. |
| `readonly root?` | `string` | The project root, absolute, when it is not the file's directory. |
| `readonly text` | `string` | The text. |
| `readonly options?` | `unknown` | The rule's options. |
| `readonly settings?` | `Readonly<Record<string, unknown>>` | The settings. |
| `readonly language?` | `Partial<LanguageSettings>` | How the text is read, where it differs from the default. |
| `readonly errors` | `unknown` | The messages, in position order. |
| `readonly output?` | `string` | The text after the rule's fixes when it fixes, null when it must offer no fix; unchecked when absent. |

##### RuleCase

A text the rule reads.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly name` | `string` | The test's name. |
| `readonly path` | `string` | The absolute path the text is linted as; its directory is the project root unless `root` says otherwise. |
| `readonly root?` | `string` | The project root, absolute, when it is not the file's directory. |
| `readonly text` | `string` | The text. |
| `readonly options?` | `unknown` | The rule's options. |
| `readonly settings?` | `Readonly<Record<string, unknown>>` | The settings. |
| `readonly language?` | `Partial<LanguageSettings>` | How the text is read, where it differs from the default. |

##### RuleCases

The cases of one rule.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `readonly valid` | `unknown` | Texts that must report nothing. |
| `readonly invalid` | `unknown` | Texts that must report. |

---

Canonical page: https://www.hyperfrontend.dev/docs/libraries/linter/
This file: https://www.hyperfrontend.dev/docs/libraries/linter.md
Documentation index: https://www.hyperfrontend.dev/llms.txt
