New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: friendlier error message when command not found #6887
Conversation
const nearestCommand = getNearestCommand(params[0], (await readProjectManifestOnly(opts.dir)).scripts) | ||
if (nearestCommand) { | ||
err.hint = `Did you mean "pnpm exec ${nearestCommand}"?` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't make sense. The pnpm exec
command is not for running scripts. pnpm exec
is for running binaries from node_modules/.bin
or just running any commands. For running scripts there is the pnpm run
command.
@zkochan Ideally, if there is a way to distinguish between |
You should be able to distinguish if you pass some option to exec handler from the run handler. |
The CI always fail, but the logs doesn't tell me where. |
I guess there's nothing we can do about it. |
This test fails: "pnpm exec command not found" Locally fails too. |
Oh. I think I know why I didn't found failed tests last time. I searched for "Failed", but jest only outputs |
@zkochan Almost all tests have passed. The ones that failed are because of timeout. This make me think: Currently, CI tests run in a matrix of 3 node versions ✕ 2 OSes ✕ 2 events = 12 parallel jobs. I think we can skip some of them to reduce timeouts. For example: On Windows, only node 20 runs, the rest is skipped. Another alternative is serialize the node versions: node 20 is tested first, then switch to 18, then 16. |
We can serialize but it will probably make the wait longer. |
exec/plugin-commands-script-runners/src/buildCommandNotFoundHint.ts
Outdated
Show resolved
Hide resolved
|
||
export async function getNearestProgram (programName: string) { | ||
try { | ||
const binDir = path.join(process.cwd(), 'node_modules', '.bin') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not enough. You should check the list of commands in the current project's node_modules and in the node_modules of the workspace root.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to get the workspace root?
This is for a hint. If the code for getting workspace root turns out to be too complicated, I think we should not implement it. After all, an incomplete but correct hint is better than an incorrect hint and a greater possibility for bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
workspaceDir
or lockfileDir
should be already passed through options.
err.hint = buildCommandNotFoundHint(params[0], (await readProjectManifestOnly(opts.dir)).scripts) | ||
err.message = `Command "${params[0]}" not found` | ||
if (opts.implicitlyFellbackFromRun) { | ||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this try needed? There is already a try/catch in getNearestProgram
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I think readProjectManifestOnly
interacts with the filesystem and therefore can fail.
Co-authored-by: Zoltan Kochan <z@kochan.io>
let programList!: string[] | ||
if (workspaceDir && workspaceDir !== dir) { | ||
const workspaceBinDir = path.join(workspaceDir, 'node_modules', '.bin') | ||
const programListExtension = await readdir(workspaceBinDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is OK if you just use readdir synchronously here and make the function synchronous. We don't care about the performance here so much.
performance is not critical here
const programList = readdirSync(binDir) | ||
if (workspaceDir && workspaceDir !== dir) { | ||
const workspaceBinDir = path.join(workspaceDir, 'node_modules', '.bin') | ||
programList.push(...readdirSync(workspaceBinDir)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is not as easy as reading all files from the .bin
directory. On Windows the same command will be duplicated a few times with different extensions: .exe
, .cmd
, maybe even .ps1
. So we need to remove the extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the didYouMean
function handle duplication, especially when we only need 1 result (the top result)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if it can handle duplication. I would deduplicated before passing it the array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are linting errors. |
exec/plugin-commands-script-runners/src/buildCommandNotFoundHint.ts
Outdated
Show resolved
Hide resolved
The tests were mostly stable in the past but for the last 2-3 days the audit tests are failing very frequently. Maybe the npm registry is rate limiting requests to the audit endpoint. |
Tysm @KSXGitHub and @zkochan 🙌 Really appreciate this !! |
related comment: #6887 (comment)
Related comment: scaffold-eth/scaffold-eth-2#444 (comment)
Initially, I have hoped that I can create a hint for
pnpm $script
in general, but since I don't know how to distinguish betweenpnpm $script
andpnpm exec $script
, I have to settle forpnpm exec $script
only.