diff --git a/.spellcheckerrc.yml b/.spellcheckerrc.yml deleted file mode 100644 index 749260f..0000000 --- a/.spellcheckerrc.yml +++ /dev/null @@ -1,5 +0,0 @@ -files: - - "**/*.md" - - "!test/**/*.md" -dictionaries: - - dictionary.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0470abb..f188716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Add support for configuration in `package.json` ([@gadhagod](https://github.com/gadhagod)). + ## [4.8.0] - 2021-06-13 - Convert project to TypeScript. diff --git a/README.md b/README.md index 634c1cb..f7d16c8 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Spellchecker CLI can also read configuration from a JSON or YAML file. By defaul You can specify any command line option in a config file. Just make sure to use camelcase option names in the config file, _e.g._ `frontmatterKeys` instead of `frontmatter-keys`. +Also, you can specify command line options in your `package.json`. All keys under `spellchecker` in `package.json` are rendered the same way as other configuration-from-file methods. + Command line arguments will override any configuration read from a file. ### Globs diff --git a/lib/config/file.ts b/lib/config/file.ts index 9482ce9..fb0cdb7 100644 --- a/lib/config/file.ts +++ b/lib/config/file.ts @@ -33,12 +33,11 @@ export const readConfigFile = (filePathFromArgs: string|undefined): ExternalConf if (filePathFromArgs) { return tryLoad(filePathFromArgs); } - const filePath = [ '/.spellcheckerrc.yaml', '/.spellcheckerrc.yml', - './spellcheckerrc.json', - './spellcheckerrc.jsonc', + '/.spellcheckerrc.json', + '/.spellcheckerrc.jsonc', ] .map(path => appRootPath.resolve(path)) .find((path) => { diff --git a/lib/config/index.ts b/lib/config/index.ts index 023f50d..1629679 100644 --- a/lib/config/index.ts +++ b/lib/config/index.ts @@ -8,6 +8,7 @@ import { defaultPlugins, getUsage, readArgs, supportedLanguages, supportedPlugins, } from './command-line'; import { readConfigFile } from './file'; +import { readFromPackage } from './package'; import { InternalConfig } from './types'; const defaultValues = { @@ -23,7 +24,8 @@ const defaultValues = { export const parseConfig = (): InternalConfig => { const args = readArgs(); const configFile = readConfigFile(args.config); - const parsedArgs = merge({}, defaultValues, configFile, args); + const packageFile = readFromPackage(); + const parsedArgs = merge({}, defaultValues, packageFile, configFile, args); const { files, diff --git a/lib/config/package.ts b/lib/config/package.ts new file mode 100644 index 0000000..448fad3 --- /dev/null +++ b/lib/config/package.ts @@ -0,0 +1,31 @@ +import { accessSync, readFileSync } from 'fs'; + +import appRootPath from 'app-root-path'; + +import { printError } from '../print-error'; + +import { ExternalConfig } from './types'; + +export function readFromPackage(): ExternalConfig { + const path = appRootPath.resolve('package.json'); + try { + accessSync(path); + } catch { + return {}; + } + let config; + try { + config = JSON.parse(readFileSync(path, 'utf-8')); + } catch (err) { + if ((err as Error).name === 'SyntaxError') { + printError('Unable to parse package.json'); + } else { + printError(`Unable to parse package.json: ${(err as Error).message}`); + } + process.exit(1); + } + if ('spellchecker' in config) { + return config.spellchecker; + } + return {}; +} diff --git a/package.json b/package.json index 8364f38..6765812 100644 --- a/package.json +++ b/package.json @@ -74,5 +74,14 @@ "mocha.parallel": "0.15.5", "ts-node": "^10.0.0", "typescript": "^4.3.2" + }, + "spellchecker": { + "files": [ + "**/*.md", + "!test/**/*.md" + ], + "dictionaries": [ + "dictionary.txt" + ] } } diff --git a/test/cli-test.ts b/test/cli-test.ts index 8e7cfe0..c9fe23e 100644 --- a/test/cli-test.ts +++ b/test/cli-test.ts @@ -21,12 +21,12 @@ chai.should(); type CommandResult = { stdout: string, stderr: string } & Partial -function runCommand(command: string): Promise { +function runCommand(command: string, appRootPath?: string): Promise { return new Promise((resolve) => { exec( command, // Prevent Spellchecker from picking up .spellcheckerrc.yml in these tests. - { env: Object.assign({}, process.env, { APP_ROOT_PATH: __dirname }) }, + { env: Object.assign({}, process.env, { APP_ROOT_PATH: appRootPath ?? __dirname }) }, (error, stdout, stderr) => { if (error) { resolve(merge({}, error, { stdout, stderr })); @@ -37,8 +37,8 @@ function runCommand(command: string): Promise { }); } -function runWithArguments(args: string) { - return runCommand(`node build/index.js ${args}`); +function runWithArguments(args: string, appRootPath?: string) { + return runCommand(`node build/index.js ${args}`, appRootPath); } const notSpell = (plugin: string) => plugin !== 'spell'; @@ -477,4 +477,9 @@ parallel('Spellchecker CLI', function testSpellcheckerCLI(this: { timeout(n: num const result = await runWithArguments('--config test/fixtures/config/basic.jsonc'); result.should.not.have.property('code'); }); + + it('can read options from `package.json`', async () => { + const result = await runWithArguments('', `${__dirname}/fixtures/config`); + result.should.not.have.property('code'); + }); }); diff --git a/test/fixtures/config/package.json b/test/fixtures/config/package.json new file mode 100644 index 0000000..2a0539d --- /dev/null +++ b/test/fixtures/config/package.json @@ -0,0 +1,11 @@ +{ + "name": "spellchecker-cli-test-fixtures", + "version": "1.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "MIT" +}