# nginx-ast

<p align="center" width="100%">
  <img src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" height="250">
</p>

TypeScript Nginx configuration parser and deparser. Parse nginx.conf files into AST and regenerate configuration from AST.

## Installation

```bash
npm install nginx-ast
```

## Usage

### Parse Nginx Configuration

```typescript
import { parse } from 'nginx-ast';

const config = `
server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://backend;
    }
}
`;

const ast = parse(config);
console.log(ast);
```

### Deparse AST to Configuration

```typescript
import { parse, deparse } from 'nginx-ast';

const ast = parse(config);
const output = deparse(ast);
console.log(output);
```

### Round-Trip Testing

```typescript
import { parse, deparse, cleanTree } from 'nginx-ast';

const ast1 = parse(config);
const output = deparse(ast1);
const ast2 = parse(output);

// Compare ASTs without location info
expect(cleanTree(ast1)).toEqual(cleanTree(ast2));
```

## AST Types

### NginxConfig (Root)

```typescript
interface NginxConfig {
  type: 'NginxConfig';
  body: Statement[];
}
```

### Directive

```typescript
interface Directive {
  type: 'Directive';
  name: string;
  args: string[];
}
```

### Block Types

- `ServerBlock` - Virtual server configuration
- `LocationBlock` - URI handling with optional modifiers (=, ~, ~*, ^~, @)
- `HttpBlock` - HTTP context
- `EventsBlock` - Events context
- `StreamBlock` - TCP/UDP proxying
- `UpstreamBlock` - Backend server groups
- `MapBlock` - Variable mapping
- `IfBlock` - Conditional logic
- `TypesBlock` - MIME type definitions
- `LimitExceptBlock` - HTTP method restrictions

### Location Modifiers

```typescript
interface LocationBlock {
  type: 'LocationBlock';
  modifier?: '=' | '~' | '~*' | '^~' | '@';
  path: string;
  body: Statement[];
}
```

- `=` - Exact match
- `~` - Case-sensitive regex
- `~*` - Case-insensitive regex
- `^~` - Prefix match (no regex check)
- `@` - Named location

## Deparse Options

```typescript
interface DeparseOptions {
  indent?: string;   // Default: '    ' (4 spaces)
  newline?: string;  // Default: '\n'
}

const output = deparse(ast, { indent: '  ' });
```

## Utilities

### cleanTree

Remove location/range information from AST for comparison:

```typescript
import { cleanTree } from 'nginx-ast';

const cleaned = cleanTree(ast);
```

### astEqual

Compare two ASTs ignoring location info:

```typescript
import { astEqual } from 'nginx-ast';

if (astEqual(ast1, ast2)) {
  console.log('ASTs are equivalent');
}
```

## License

MIT

---

## Development

### Setup

1. Clone the repository:

```bash
git clone https://github.com/constructive-io/dev-utils.git
```

2. Install dependencies:

```bash
cd dev-utils
pnpm install
pnpm build
```

3. Test the package of interest:

```bash
cd packages/<packagename>
pnpm test:watch
```

## Credits

**🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**

## Disclaimer

AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.

No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
