Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b719e0a
first commit
pyramation Jan 9, 2026
df9a29e
feat: add docker-parser and bash-parser packages with heterogeneous p…
pyramation Jan 9, 2026
d4307b2
fix: update CI workflow for new package names
pyramation Jan 9, 2026
748ab89
Merge pull request #1 from constructive-io/devin/1767978775-docker-ba…
pyramation Jan 9, 2026
bca2c10
feat: add policy-engine and nginx-parser packages
pyramation Jan 9, 2026
11d8b52
Merge pull request #2 from constructive-io/devin/1767980277-improvements
pyramation Jan 9, 2026
8faedbe
feat: add safegres plan and update licenses to All Rights Reserved
pyramation Jan 9, 2026
7100f8b
Merge pull request #3 from constructive-io/devin/1767985210-safegres-…
pyramation Jan 9, 2026
4e8cf93
feat: add safegres package with unified security types
pyramation Jan 9, 2026
b12774e
chore: update pnpm-lock.yaml for safegres package
pyramation Jan 9, 2026
875ae3e
wip
pyramation Jan 10, 2026
02f54e9
readme
pyramation Jan 10, 2026
1f8a109
Merge pull request #5 from constructive-io/devin/1767985858-safegres-…
pyramation Jan 10, 2026
fb07036
docs: update README with safegres architecture and mermaid diagrams
pyramation Jan 10, 2026
94590b3
docs: add safegres brand header to all package READMEs
pyramation Jan 10, 2026
e122d2b
Merge main into feature branch - resolve README conflicts
pyramation Jan 10, 2026
25746c7
Merge pull request #6 from constructive-io/devin/1767985858-safegres-…
pyramation Jan 10, 2026
9554a5a
Import bash-parser, docker-parser, nginx-parser from constructive-io/…
pyramation Apr 27, 2026
4a2c8d6
chore: align parser packages with dev-utils conventions (makage build…
pyramation Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ jobs:
- genomic
- inquirerer-utils
- create-gen-app-test
- bash-parser
- docker-parser
- nginx-parser

steps:
- name: Checkout code
Expand Down
85 changes: 85 additions & 0 deletions packages/bash-parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# bash-parser

<p align="center" width="100%">
<img height="250" src="https://raw.githubusercontent.com/Safegres/brand/refs/heads/main/safegres.svg" />
</p>

Bash/shell command parser and deparser for JavaScript/TypeScript.

## Installation

```bash
npm install bash-parser
```

## Usage

### Parsing

```typescript
import { parse } from 'bash-parser';

const source = 'echo hello | grep h && ls -la';
const ast = parse(source);
console.log(JSON.stringify(ast, null, 2));
```

### Deparsing

```typescript
import { parse, deparse } from 'bash-parser';

const source = 'echo hello';
const ast = parse(source);
const output = deparse(ast);
console.log(output); // echo hello
```

### AST Comparison

```typescript
import { parse, cleanTree } from 'bash-parser';

const ast1 = parse('echo hello');
const ast2 = parse('echo hello');

const clean1 = cleanTree(ast1);
const clean2 = cleanTree(ast2);

// Compare ASTs without position information
console.log(JSON.stringify(clean1) === JSON.stringify(clean2)); // true
```

## Supported Constructs

### Commands
- Simple commands with arguments
- Pipelines (`cmd1 | cmd2`)
- Logical operators (`&&`, `||`)
- Subshells (`(cmd)`)
- Brace groups (`{ cmd; }`)

### Control Flow
- if/then/else/elif/fi
- while/do/done
- until/do/done
- for/in/do/done
- case/esac

### Redirections
- Input (`<`)
- Output (`>`, `>>`)
- Here strings (`<<<`)
- File descriptor redirects (`2>&1`)

### Other
- Variable assignments (`VAR=value`)
- Function definitions
- Quoted strings (single and double)
- Variable expansion (`$VAR`, `${VAR}`)
- Command substitution (`$(cmd)`, `` `cmd` ``)
- Arithmetic expansion (`$((expr))`)

## License

MIT
110 changes: 110 additions & 0 deletions packages/bash-parser/__tests__/deparser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { deparse } from '../src/deparser';
import { parse } from '../src/parser';

describe('bash-deparser', () => {
describe('deparse', () => {
it('should deparse simple command', () => {
const ast = parse('echo hello');
const result = deparse(ast);
expect(result).toBe('echo hello');
});

it('should deparse command with multiple arguments', () => {
const ast = parse('ls -la /tmp');
const result = deparse(ast);
expect(result).toBe('ls -la /tmp');
});

it('should deparse pipeline', () => {
const ast = parse('cat file | grep pattern');
const result = deparse(ast);
expect(result).toBe('cat file | grep pattern');
});

it('should deparse logical AND', () => {
const ast = parse('cmd1 && cmd2');
const result = deparse(ast);
expect(result).toBe('cmd1 && cmd2');
});

it('should deparse logical OR', () => {
const ast = parse('cmd1 || cmd2');
const result = deparse(ast);
expect(result).toBe('cmd1 || cmd2');
});

it('should deparse redirect output', () => {
const ast = parse('echo hello > file.txt');
const result = deparse(ast);
expect(result).toBe('echo hello >file.txt');
});

it('should deparse redirect input', () => {
const ast = parse('cat < input.txt');
const result = deparse(ast);
expect(result).toBe('cat <input.txt');
});

it('should deparse append redirect', () => {
const ast = parse('echo hello >> file.txt');
const result = deparse(ast);
expect(result).toBe('echo hello >>file.txt');
});

it('should deparse assignment', () => {
const ast = parse('VAR=value');
const result = deparse(ast);
expect(result).toBe('VAR=value');
});

it('should deparse assignment with command', () => {
const ast = parse('VAR=value echo $VAR');
const result = deparse(ast);
expect(result).toBe('VAR=value echo $VAR');
});

it('should deparse subshell', () => {
const ast = parse('(echo hello)');
const result = deparse(ast);
expect(result).toBe('(echo hello)');
});

it('should deparse if statement', () => {
const ast = parse('if test -f file; then echo exists; fi');
const result = deparse(ast);
expect(result).toContain('if');
expect(result).toContain('then');
expect(result).toContain('fi');
});

it('should deparse while loop', () => {
const ast = parse('while true; do echo loop; done');
const result = deparse(ast);
expect(result).toContain('while');
expect(result).toContain('do');
expect(result).toContain('done');
});

it('should deparse for loop', () => {
const ast = parse('for i in 1 2 3; do echo $i; done');
const result = deparse(ast);
expect(result).toContain('for');
expect(result).toContain('in');
expect(result).toContain('do');
expect(result).toContain('done');
});

it('should deparse function definition', () => {
const ast = parse('myfunc() { echo hello; }');
const result = deparse(ast);
expect(result).toContain('myfunc()');
});

it('should deparse multiple commands', () => {
const ast = parse('echo one; echo two');
const result = deparse(ast);
expect(result).toContain('echo one');
expect(result).toContain('echo two');
});
});
});
Loading
Loading