# helpers

What a rule asks of a file beyond the tree it is handed: the tokens and comments around a node, scopes and variables, the properties of a JSON document, line and column positions, globs, a fix merged from several edits, and the parse of another file. The packaged rules use the same functions.

## Usage

```typescript
import { defineRule } from '@hyperfrontend/linter'
import { findVariable, scopeAnalysisOf, scopeOf } from '@hyperfrontend/linter/helpers'

export default defineRule({
  id: 'local/no-shadowed-name',
  language: 'code',
  fixable: false,
  create: (context) => ({
    Identifier(node) {
      if (node.name !== 'name') return
      const scope = scopeOf(scopeAnalysisOf(context), node)
      if (findVariable(scope, 'name') === null) return
      const message = 'Rename the local "name".'
      context.report({ start: node.start, end: node.end, message })
    },
  }),
})
```

## What is here

| Area      | Functions                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Tokens    | [`tokenBefore`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-tokenBefore), [`tokenAfter`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-tokenAfter), [`tokensBetween`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-tokensBetween), [`tokenText`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-tokenText), [`commentsBetween`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-commentsBetween), [`isParenthesised`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-isParenthesised) |
| Tree      | [`childrenOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-childrenOf), [`parseCode`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-parseCode)                                                                                                                                                                                                                                                                                                                                                                                                                 |
| Scopes    | [`scopeAnalysisOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-scopeAnalysisOf), [`scopeOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-scopeOf), [`findVariable`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-findVariable), [`referenceOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-referenceOf), [`isGlobalReference`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-isGlobalReference)                                                                                                |
| JSON      | [`rootObject`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-rootObject), [`propertyOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-propertyOf), [`isString`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-isString), [`stringsUnder`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-stringsUnder), [`wholeDocument`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-wholeDocument), [`indentationAt`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-indentationAt)               |
| Positions | [`positionAt`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-positionAt), [`lineText`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-lineText)                                                                                                                                                                                                                                                                                                                                                                                                                   |
| Globs     | [`compileGlob`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-compileGlob), [`matchesAny`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-matchesAny)                                                                                                                                                                                                                                                                                                                                                                                                             |
| Fixes     | [`mergeEdits`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-mergeEdits)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

Scope analysis is computed once per file and shared by every rule that asks for it, so calling [`scopeAnalysisOf`](https://www.hyperfrontend.dev/docs/libraries/linter/helpers/#api-scopeAnalysisOf) from several listeners costs one analysis.

## API Reference

### 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
```

---

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