Repository: gohugoio/hugo (language: go)

Issue:
Fall back to trying ex.New(binaryName, cmdArgs...) when ex.Npx fails for PostCSS etc.
I want to use a theme (https://github.com/google/docsy) but I don't want to install any NodeJS garbage on my host machine. 

As a workaround, I created a Dockerfile, wrapped script and rebuilt hugo
```Dockerfile
FROM node:20-alpine

WORKDIR /usr/src/app

RUN npm install -g postcss postcss-cli autoprefixer

CMD [""]
```

```bash
docker build -t postcss .
```

❯ cat postcss 
```
#!/bin/bash
docker run --rm -i postcss postcss "$@"
```

Rebuild Hugo with a changed next-line because `Npx` function adding the npx prefix that I don't need.
https://github.com/gohugoio/hugo/blob/ee26e69ce3367ebfe1e35f03695d51b049aab955/resources/resource_transformers/postcss/postcss.go#L203
=>
```go
ex.New(binaryName, cmdArgs...)
```

**It'd be nice to have a configurable binary path to the `postcss` to make it possible to call without npx prefix.** 




Discussion / hints:
I'm experiencing a similar drawback when working from within this dev container.

```json
{
  "name": "Build environment",
  "image": "mcr.microsoft.com/devcontainers/base:bullseye",
  "features": {
  "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/go:1": {},
    "ghcr.io/devcontainers/features/hugo:1": {
      "extended": true
    },
    "ghcr.io/devcontainers/features/node:1": {}
  }
}
```

`postCSS` filters are not applied and `main.scss` does not build a `main.css` file.
I ran into this myself. I can't use the [postcss-cli package from nixpkgs](https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=postcss), because `resources.PostCSS` shells out to `npx` to run `postcss`. An easy fix would be to make hugo's calls for `npx` binaries fall back to checking the user's `PATH` for binaries by the same name - it would be backwards compatible and afaik would follow hugo's security model.

An aside:
I'm curious what @bep's plan is for the new [tailwind oxide engine](https://discourse.gohugo.io/t/hugo-pipes-and-tailwind-s-upcoming-oxide-engine/47802). The only reason I have a `package.json` with 
```
  "devDependencies": {
    "@fullhuman/postcss-purgecss": "^5.0.0",
    "@tailwindcss/typography": "^0.5.13",
    "autoprefixer": "^10.4.19",
    "postcss": "^8.4.38",
    "postcss-cli": "^11.0.0",
    "postcss-html": "^1.7.0",
    "tailwindcss": "^3.4.4"
  },
```
is because of tailwind. Once we get the new tailwind engine, I expect it will warrant its own [`resource_transformer`](https://github.com/gohugoio/hugo/tree/master/resources/resource_transformers). I realize @kurotych, @almereyda, and I are the 1% of the 5% who care, but I'm hoping it won't depend on `npx`; I could ditch the whole npm/nodejs toolchain.

Related discussion:
- https://github.com/gohugoio/hugo/issues/796
- https://github.com/gohugoio/hugo/issues/8299
- https://github.com/gohugoio/hugo/issues/10757
- https://discourse.gohugo.io/t/add-a-way-to-import-npm-modules-as-hugo-modules/43878/2
- https://discourse.gohugo.io/t/hugo-pipes-and-tailwind-s-upcoming-oxide-engine/47802
>I'm curious what @bep's plan is for the new [tailwind oxide engine](https://discourse.gohugo.io/t/hugo-pipes-and-tailwind-s-upcoming-oxide-engine/47802). 

Odd timing, I was just testing Tailwind Oxide:

https://github.com/bep/hugo-starter-tailwind-basic/pull/19

It's convenient to have `npm` install and maintain the version of this stuff, but if we could get rid of `PostCSS` for TailwindCSS, that would be pretty great. A first glance it looks like the Tailwind CLI lacks some options in this area, but that could be fixed.

As to this issue:

>It'd be nice to have a configurable binary path to the postcss to make it possible to call without npx prefix.

Hugo currently only executes predefined _binary names_ that's in the OS `PATH`. Having a way to execute arbitrary binaries (even if that could be controlled) is not something I'm opening up for. We could/should probably have a `useNpx` or something, but I'm not sure where to put it.


>❯ cat postcss

Why not create a `npx` wrapper that do similar?

I don't want to speak for @kurotych, but given that they are already building and running a docker container with a `postcss` shellscript, adding an `npx`  wrapper doesn't seem like much of an additional lift. 

_However_, I think both of our problems would be solved easily and securely by falling back to checking for a binary _of the same name_ using `ex.New(binaryName, cmdArgs...)`, when the following fails:

https://github.com/gohugoio/hugo/blob/af0cb57aaf668278551454678eac60b17348b13c/resources/resource_transformers/postcss/postcss.go#L203-L210

Basically:
1. check if postcss exists with npx (already done)
2. failing that, check if it is installed globally
>However, I think both of our problems would be solved easily and securely by falling back to checking for a binary of the same name using ex.New(binaryName, cmdArgs...)

I agree. I'm going to repurpose this issue some something that's both simple and actionable, and if that does not fix it, we'll revisit later.

Fix patch:
diff --git a/common/hexec/exec.go b/common/hexec/exec.go
index 12775bd1ce8..49291354dd0 100644
--- a/common/hexec/exec.go
+++ b/common/hexec/exec.go
@@ -143,8 +143,18 @@ func (e *Exec) New(name string, arg ...any) (Runner, error) {
 	return cm.command(arg...)
 }
 
-// Npx is a convenience method to create a Runner running npx --no-install <name> <args.
+// Npx will try to run npx, and if that fails, it will
+// try to run the binary directly.
 func (e *Exec) Npx(name string, arg ...any) (Runner, error) {
+	r, err := e.npx(name, arg...)
+	if err == nil {
+		return r, nil
+	}
+	return e.New(name, arg...)
+}
+
+// npx is a convenience method to create a Runner running npx --no-install <name> <args.
+func (e *Exec) npx(name string, arg ...any) (Runner, error) {
 	arg = append(arg[:0], append([]any{"--no-install", name}, arg[0:]...)...)
 	return e.New("npx", arg...)
 }


Test patch: