Repository: typst/typst
File: crates/typst-cli/src/compile.rs (Rust)

Code before:
#[typst_macros::time(name = "compile once")]
pub fn compile_once(
    world: &mut SystemWorld,
    command: &mut CompileCommand,
    watching: bool,
) -> StrResult<()> {
    let start = std::time::Instant::now();
    if watching {
        Status::Compiling.print(command).unwrap();
    }

    // Check if main file can be read and opened.
    let main_result = world.source(world.main());
    if main_result.is_err() {
        set_failed();
        if watching {
            Status::Error.print(command).unwrap();
        }

        let errors = match main_result {
            Err(FileError::InvalidUtf8) => match &command.common.input {
                Input::Path(path) => {
                    let extension = path.extension();
                    let mut errors = main_result.at(Span::detached()).unwrap_err();
                    if let Some(extension) = extension {
                        if extension != "typ" {
                            errors
                                .make_mut()
                                .first_mut()
                                .unwrap()
                                .hint(eco_format!("a file with the `.{}` extension is not usually a Typst file. Check if you meant to use `.typ` instead.", extension.to_string_lossy()));
                        }
                    } else {
                        errors
                            .make_mut()
                            .first_mut()
                            .unwrap()
                            .hint("a file without an extension (`.something`) is not usually a Typst file. Check if you meant to add `.typ` to its name.");
                    }

                    errors
                }
                _ => {
                    // We are only interested in providing helpful errors
                    // when users try to load an invalid path.
                    main_result.at(Span::detached()).unwrap_err()
                }
            },

            main_result => main_result.at(Span::detached()).unwrap_err(),
        };

Review comment (suggestion):
```suggestion
                        if extension != "typ" && extension != "typst" {
```
Some people may use `.typst` instead of `.typ`.

Code after:
#[typst_macros::time(name = "compile once")]
pub fn compile_once(
    world: &mut SystemWorld,
    command: &mut CompileCommand,
    watching: bool,
) -> StrResult<()> {
    let start = std::time::Instant::now();
    if watching {
        Status::Compiling.print(command).unwrap();
    }

    if let Err(errors) = world
        .source(world.main())
        .map_err(|err| hint_invalid_main_file(err, &command.common.input))
    {
        set_failed();
        if watching {
            Status::Error.print(command).unwrap();
        }

        print_diagnostics(world, &errors, &[], command.common.diagnostic_format)
            .map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;

        return Ok(());
    }

    let mut tracer = Tracer::new();
    let result = typst::compile(world, &mut tracer);
    let warnings = tracer.warnings();

    match result {
        // Export the PDF / PNG.
        Ok(document) => {
            export(world, &document, command, watching)?;
            let duration = start.elapsed();

            if watching {
                if warnings.is_empty() {
                    Status::Success(duration).print(command).unwrap();
                } else {
                    Status::PartialSuccess(duration).print(command).unwrap();
                }
            }

            print_diagnostics(world, &[], &warnings, command.common.diagnostic_format)
                .map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;

            write_make_deps(world, command)?;

            if let Some(open) = command.open.take() {
                if let Output::Path(file) = command.output() {