Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ Follows the `json` module convention. All option parameters are keyword-only.

## CLI

Console scripts defined in `pyproject.toml`. Each uses argparse flags that map directly to the option dataclass fields above.
Console scripts defined in `pyproject.toml`. Both accept one or more positional `PATH` arguments (files, directories, or `-` for stdin) and an optional `-o`/`--output` flag. Additional option flags map directly to the option dataclass fields above.

```
hcl2tojson file.tf # single file to stdout
hcl2tojson a.tf b.tf -o out/ # multiple files to output dir
hcl2tojson --json-indent 2 --with-meta file.tf
jsontohcl2 --indent 4 --no-align file.json
```
Expand Down
65 changes: 44 additions & 21 deletions cli/hcl_to_json.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""``hcl2tojson`` CLI entry point — convert HCL2 files to JSON."""

import argparse
import json
import os
Expand All @@ -11,6 +12,7 @@
HCL_SKIPPABLE,
_convert_single_file,
_convert_directory,
_convert_multiple_files,
_convert_stdin,
)

Expand All @@ -35,12 +37,14 @@ def main():
)
parser.add_argument(
"PATH",
help='The file or directory to convert (use "-" for stdin)',
nargs="+",
help='One or more files or directories to convert (use "-" for stdin)',
)
parser.add_argument(
"OUT_PATH",
nargs="?",
help="The path to write output to. Optional for single file (defaults to stdout)",
"-o",
"--output",
dest="output",
help="Output path (file for single input, directory for multiple inputs)",
)
parser.add_argument("--version", action="version", version=__version__)

Expand Down Expand Up @@ -118,21 +122,40 @@ def main():
def convert(in_file, out_file):
_hcl_to_json(in_file, out_file, options, json_indent=json_indent)

if args.PATH == "-":
_convert_stdin(convert)
elif os.path.isfile(args.PATH):
_convert_single_file(
args.PATH, args.OUT_PATH, convert, args.skip, HCL_SKIPPABLE
)
elif os.path.isdir(args.PATH):
_convert_directory(
args.PATH,
args.OUT_PATH,
convert,
args.skip,
HCL_SKIPPABLE,
in_extensions={".tf", ".hcl"},
out_extension=".json",
)
paths = args.PATH
output = args.output

if len(paths) == 1:
path = paths[0]
if path == "-":
_convert_stdin(convert)
elif os.path.isfile(path):
_convert_single_file(path, output, convert, args.skip, HCL_SKIPPABLE)
elif os.path.isdir(path):
_convert_directory(
path,
output,
convert,
args.skip,
HCL_SKIPPABLE,
in_extensions={".tf", ".hcl"},
out_extension=".json",
)
else:
raise RuntimeError(f"Invalid Path: {path}")
else:
raise RuntimeError(f"Invalid Path: {args.PATH}")
for p in paths:
if not os.path.isfile(p):
raise RuntimeError(f"Invalid file: {p}")
if output is None:
for p in paths:
_convert_single_file(p, None, convert, args.skip, HCL_SKIPPABLE)
else:
_convert_multiple_files(
paths,
output,
convert,
args.skip,
HCL_SKIPPABLE,
out_extension=".json",
)
22 changes: 20 additions & 2 deletions cli/helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Shared file-conversion helpers for the HCL2 CLI commands."""

import json
import os
import sys
from typing import Callable, IO, Set, Tuple, Type
from typing import Callable, IO, List, Optional, Set, Tuple, Type

from lark import UnexpectedCharacters, UnexpectedToken

Expand Down Expand Up @@ -50,7 +51,7 @@ def _convert_directory(
out_extension: str,
) -> None:
if out_path is None:
raise RuntimeError("Positional OUT_PATH parameter shouldn't be empty")
raise RuntimeError("Output path is required for directory conversion (use -o)")
if not os.path.exists(out_path):
os.mkdir(out_path)

Expand Down Expand Up @@ -91,6 +92,23 @@ def _convert_directory(
raise


def _convert_multiple_files(
in_paths: List[str],
out_path: str,
convert_fn: Callable[[IO, IO], None],
skip: bool,
skippable: Tuple[Type[BaseException], ...],
out_extension: str,
) -> None:
"""Convert multiple files into an output directory."""
if not os.path.exists(out_path):
os.makedirs(out_path)
for in_path in in_paths:
base = os.path.splitext(os.path.basename(in_path))[0] + out_extension
file_out = os.path.join(out_path, base)
_convert_single_file(in_path, file_out, convert_fn, skip, skippable)


def _convert_stdin(convert_fn: Callable[[IO, IO], None]) -> None:
convert_fn(sys.stdin, sys.stdout)
sys.stdout.write("\n")
65 changes: 44 additions & 21 deletions cli/json_to_hcl.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""``jsontohcl2`` CLI entry point — convert JSON files to HCL2."""

import argparse
import json
import os
Expand All @@ -12,6 +13,7 @@
JSON_SKIPPABLE,
_convert_single_file,
_convert_directory,
_convert_multiple_files,
_convert_stdin,
)

Expand All @@ -36,12 +38,14 @@ def main():
)
parser.add_argument(
"PATH",
help='The file or directory to convert (use "-" for stdin)',
nargs="+",
help='One or more files or directories to convert (use "-" for stdin)',
)
parser.add_argument(
"OUT_PATH",
nargs="?",
help="The path to write output to. Optional for single file (defaults to stdout)",
"-o",
"--output",
dest="output",
help="Output path (file for single input, directory for multiple inputs)",
)
parser.add_argument("--version", action="version", version=__version__)

Expand Down Expand Up @@ -116,21 +120,40 @@ def main():
def convert(in_file, out_file):
_json_to_hcl(in_file, out_file, d_opts, f_opts)

if args.PATH == "-":
_convert_stdin(convert)
elif os.path.isfile(args.PATH):
_convert_single_file(
args.PATH, args.OUT_PATH, convert, args.skip, JSON_SKIPPABLE
)
elif os.path.isdir(args.PATH):
_convert_directory(
args.PATH,
args.OUT_PATH,
convert,
args.skip,
JSON_SKIPPABLE,
in_extensions={".json"},
out_extension=".tf",
)
paths = args.PATH
output = args.output

if len(paths) == 1:
path = paths[0]
if path == "-":
_convert_stdin(convert)
elif os.path.isfile(path):
_convert_single_file(path, output, convert, args.skip, JSON_SKIPPABLE)
elif os.path.isdir(path):
_convert_directory(
path,
output,
convert,
args.skip,
JSON_SKIPPABLE,
in_extensions={".json"},
out_extension=".tf",
)
else:
raise RuntimeError(f"Invalid Path: {path}")
else:
raise RuntimeError(f"Invalid Path: {args.PATH}")
for p in paths:
if not os.path.isfile(p):
raise RuntimeError(f"Invalid file: {p}")
if output is None:
for p in paths:
_convert_single_file(p, None, convert, args.skip, JSON_SKIPPABLE)
else:
_convert_multiple_files(
paths,
output,
convert,
args.skip,
JSON_SKIPPABLE,
out_extension=".tf",
)
29 changes: 19 additions & 10 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,19 +215,23 @@ text = hcl2.reconstruct(tree)

### hcl2tojson

Convert HCL2 files to JSON.
Convert HCL2 files to JSON. Accepts one or more files, a directory, or stdin.

```sh
hcl2tojson main.tf # print JSON to stdout
hcl2tojson main.tf output.json # write to file
hcl2tojson terraform/ output/ # convert a directory
cat main.tf | hcl2tojson - # read from stdin
hcl2tojson main.tf # single file to stdout
hcl2tojson main.tf -o output.json # single file to output file
hcl2tojson terraform/ -o output/ # convert a directory
hcl2tojson a.tf b.tf # multiple files to stdout
hcl2tojson a.tf b.tf -o output/ # multiple files to output directory
hcl2tojson *.tf -o output/ # shell glob expansion
cat main.tf | hcl2tojson - # read from stdin
```

**Flags:**

| Flag | Description |
|---|---|
| `-o`, `--output` | Output path (file for single input, directory for multiple) |
| `-s` | Skip un-parsable files |
| `--json-indent N` | JSON indentation width (default: 2) |
| `--with-meta` | Add `__start_line__` / `__end_line__` metadata |
Expand All @@ -238,23 +242,28 @@ cat main.tf | hcl2tojson - # read from stdin
| `--no-preserve-heredocs` | Convert heredocs to plain strings |
| `--force-parens` | Force parentheses around all operations |
| `--no-preserve-scientific` | Convert scientific notation to standard floats |
| `--strip-string-quotes` | Strip surrounding double-quotes from string values |
| `--version` | Show version and exit |

### jsontohcl2

Convert JSON files to HCL2.
Convert JSON files to HCL2. Accepts one or more files, a directory, or stdin.

```sh
jsontohcl2 output.json # print HCL2 to stdout
jsontohcl2 output.json main.tf # write to file
jsontohcl2 output/ terraform/ # convert a directory
cat output.json | jsontohcl2 - # read from stdin
jsontohcl2 output.json # single file to stdout
jsontohcl2 output.json -o main.tf # single file to output file
jsontohcl2 output/ -o terraform/ # convert a directory
jsontohcl2 a.json b.json # multiple files to stdout
jsontohcl2 a.json b.json -o terraform/ # multiple files to output directory
jsontohcl2 *.json -o terraform/ # shell glob expansion
cat output.json | jsontohcl2 - # read from stdin
```

**Flags:**

| Flag | Description |
|---|---|
| `-o`, `--output` | Output path (file for single input, directory for multiple) |
| `-s` | Skip un-parsable files |
| `--indent N` | Indentation width (default: 2) |
| `--colon-separator` | Use `:` instead of `=` in object elements |
Expand Down
Loading
Loading