diff --git a/pyproject.toml b/pyproject.toml index 06546b0..f361eb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "foreignthon" -version = "0.2.0" +version = "0.3.0" description = "Write Python in any language. Transpiles foreign-language .xx.py files to standard Python." license = { text = "GPL v3" } requires-python = ">=3.9" diff --git a/src/foreignthon/cli.py b/src/foreignthon/cli.py index 6938e88..505d1a3 100644 --- a/src/foreignthon/cli.py +++ b/src/foreignthon/cli.py @@ -9,15 +9,17 @@ from . import __version__ from .errors import activate from .transpiler import run_transpiled, transpile_file +CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) -@click.group() + +@click.group(context_settings=CONTEXT_SETTINGS) @click.version_option(__version__, prog_name="fpy") def main(): """ForeignThon — write Python in any language.""" pass -@main.command() +@main.command(context_settings=CONTEXT_SETTINGS) @click.argument("file", type=click.Path(exists=True, path_type=Path)) @click.option("--lang", "-l", default=None, help="Override language code (e.g. es, ta)") @click.option("--keep", is_flag=True, help="Keep the compiled .py alongside the source") @@ -29,8 +31,6 @@ def run(file: Path, lang: str | None, keep: bool): source = f"# foreignthon: {lang}\n" + source file.write_text(source, encoding="utf-8") - # Activate error hook BEFORE transpiling so even transpile - # errors get shown in the foreign language detected_lang = lang or _lang_from_file(file) activate(detected_lang) @@ -44,22 +44,41 @@ def run(file: Path, lang: str | None, keep: bool): run_transpiled(file, transpiled) -@main.command() +@main.command(context_settings=CONTEXT_SETTINGS) @click.argument("file", type=click.Path(exists=True, path_type=Path)) -@click.option("--output", "-o", default=None, help="Output path (default: beside source)") +@click.option( + "--output", "-o", default=None, + help="Output file or directory. Defaults to same directory as source." +) def compile(file: Path, output: str | None): - """Transpile a file to standard Python without running it.""" + """ + Transpile a foreign-language file to standard Python. + + Output can be a file path or a directory: + + \b + fpy compile script.es.py # → script.compiled.py + fpy compile script.es.py -o out/ # → out/script.compiled.py + fpy compile script.es.py -o out.py # → out.py + """ transpiled = transpile_file(file) - out_path = ( - Path(output) if output - else file.with_suffix("").with_suffix(".compiled.py") - ) + if output is None: + out_path = file.with_suffix("").with_suffix(".compiled.py") + else: + out = Path(output) + if out.is_dir() or str(output).endswith("/"): + out.mkdir(parents=True, exist_ok=True) + out_path = out / file.with_suffix("").with_suffix(".compiled.py").name + else: + out_path = out + + out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(transpiled, encoding="utf-8") click.echo(f"Compiled: {out_path}") -@main.command() +@main.command(context_settings=CONTEXT_SETTINGS) @click.argument("file", type=click.Path(exists=True, path_type=Path)) def check(file: Path): """Validate a foreign-language file without running it.""" @@ -77,7 +96,7 @@ def check(file: Path): sys.exit(1) -@main.command("pack") +@main.command("pack", context_settings=CONTEXT_SETTINGS) @click.argument("json_file", type=click.Path(exists=True, path_type=Path)) def validate_pack(json_file: Path): """Validate a language pack JSON file."""