streetsidesoftware / cspell

A Spell Checker for Code!
https://cspell.org
MIT License
1.23k stars 92 forks source link

💡: problems invoking cspell from api #6262

Open Zamiell opened 2 days ago

Zamiell commented 2 days ago

Problem

I am the author of cspell-check-unused-words. In the script, it runs the following command:

cspell --no-progress --no-summary --unique --words-only --config cspell.temp.config.jsonc

However, this ends up being buggy because I have to physically create a "cspell.temp.config.jsonc" file on disk, which causes the Git tree to become dirty and screws up other lint tasks that are running parallel. I also can't move the temporary config to a temporary directory, since this will mess up all the no-longer-relative paths in the "ignorePaths" part of the config.

Thus, I would just like to use the CSpell API to invoke this command through JavaScript/TypeScript.

Solution

Proposal:

Add the following fields to LinterCliOptions:

Alternate Solutions

Ultimately, I would love for unused word checking to become a feature of CSpell itself. Then I wouldn't need to invoke the CSpell API at all and I could archive this repository.

Code of Conduct

Jason3S commented 1 day ago

@Zamiell,

It is possible to use cspell to do the linting, even if it is not well documented.

One of the tests does exactly that: https://github.com/streetsidesoftware/cspell/blob/5a09a9761a1ce76b3d50f34ac702a579b0e79aa3/test-packages/cspell/test-cspell/src/index.ts#L21

import {lint} from 'cspell';

lint(['*.md'], { progress: false, summary: false, wordsOnly: true, config: './cspell.temp.config.jsonc' });

You might want to add: "noConfigSearch": true to ./cspell.temp.config.jsonc

Please let me know if that works for you.

Zamiell commented 1 day ago

Hi Jason, thanks for the quick reply.

In that code snippet, you seem to manually be specifying *.md as the files to lint. However, I want to recursively lint everything in the current working directory, while also taking into account options from the CSpell configuration file such as "files", "enableFiletypes", "useGitignore", and so on. (The idea here is that we want to match the "normal" invocation of CSpell, in order to get a list of misspelled words that the end-user would normally have on their entire project all at once.) As you probably know, calculating the exact files to lint is non-trivial, as I would have to reimplement reading of the gitignore file, and so on. That's why I just want to point the lint function at a CSpell config and have it automatically calculate the correct glob/files.

Jason3S commented 1 day ago

@Zamiell,

The lint function is what the command line parser calls. All the options from the command line are just passed to the lint function. It will go through all the normal command line behavior, including reading the config file. The ['*.md'] is just the globs passed from the command line. If you don't have any globs (because you have a "files" setting), then an empty array will work: []. Search from the current directory would be: ['.'] or ['**'].

import {lint} from 'cspell';

lint([], { progress: false, summary: false, wordsOnly: true, config: './cspell.temp.config.jsonc' });
Zamiell commented 1 day ago

Ohh, perfect! I didn't understand that, thank you!

One more thing though, how do I supply the config as a JavaScript object? For this use case, I cannot have the config live on the disk, as I explained in the OP. (Having a temporary config live on the disk makes the Git tree dirty and causes other linting tasks to fail.)

Jason3S commented 1 day ago

One more thing though, how do I supply the config as a JavaScript object? For this use case, I cannot have the config live on the disk, as I explained in the OP. (Having a temporary config live on the disk makes the Git tree dirty and causes other linting tasks to fail.)

I was a bit too fast to reply. I missed the actual ask. Invoking lint from cspell is pretty easy, but as you pointed out, only the command line options are available. It is feasible to support a configObject.

Zamiell commented 1 day ago

Thank you. Perhaps after adding configObject, then config should be renamed to configPath then to make it more explicit that there are two different config properties? Alternatively, another option would be for config to take in string | Record<string, blah> | undefined instead of string | undefined. I don't have a strong preference between these.