Skip to content

Commit ab8b1fd

Browse files
authored
fix: Adjust the meaning of CSPELL_DEFAULT_CONFIG_PATH (#7240)
1 parent c7345dd commit ab8b1fd

File tree

9 files changed

+164
-95
lines changed

9 files changed

+164
-95
lines changed
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
3-
// Version of the setting file. Always 0.1
43
"version": "0.2",
5-
// language - current active spelling language
6-
"language": "en"
4+
"language": "en",
5+
"words": [
6+
"everyline"
7+
],
8+
"noConfigSearch": true
79
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
3-
// Version of the setting file. Always 0.1
43
"version": "0.2",
5-
// language - current active spelling language
6-
"language": "en"
4+
"language": "en",
5+
"noConfigSearch": true
76
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ignorePaths:
2+
- "**/cspell.*.{json,yaml,yml}"
3+
words:
4+
- everyline
5+
- includegraphics
6+
- someissues
7+
- Zotero

packages/cspell/src/app/application.mts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import { CSpellReporterConfiguration } from './models.js';
2020
import type { BaseOptions, LegacyOptions, LinterCliOptions, SuggestionOptions, TraceOptions } from './options.js';
2121
import { fixLegacy } from './options.js';
2222
import { simpleRepl } from './repl/index.js';
23-
import { fileInfoToDocument, readConfig, readFileInfo } from './util/fileHelper.js';
23+
import { readConfig } from './util/configFileHelper.js';
24+
import { fileInfoToDocument, readFileInfo } from './util/fileHelper.js';
2425
import { finalizeReporter } from './util/reporters.js';
2526
import { readStdin } from './util/stdin.js';
2627
import { getTimeMeasurer } from './util/timer.js';

packages/cspell/src/app/lint/lint.test.ts

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
55
import { CheckFailed } from '../app.mjs';
66
import { environmentKeys } from '../environment.js';
77
import { pathPackageRoot } from '../test/test.helper.js';
8-
import { readConfig } from '../util/fileHelper.js';
8+
import { readConfig } from '../util/configFileHelper.js';
99
import { InMemoryReporter } from '../util/InMemoryReporter.js';
1010
import { runLint } from './lint.js';
1111
import { LintRequest } from './LintRequest.js';
@@ -28,7 +28,6 @@ const j = path.join;
2828
describe('Linter Validation Tests', () => {
2929
afterEach(() => {
3030
delete process.env[environmentKeys.CSPELL_CONFIG_PATH];
31-
delete process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH];
3231
});
3332

3433
test('globs on the command line override globs in the config.', async () => {
@@ -50,27 +49,13 @@ describe('Linter Validation Tests', () => {
5049
const result = await runLint(new LintRequest(['**/*.txt'], options, reporter));
5150
expect(result.errors).toBe(0);
5251
expect(result.files).toBe(1);
53-
expect(result.issues).toBe(7);
54-
});
55-
56-
test('falls back to CSPELL_DEFAULT_CONFIG_PATH when no config is set', async () => {
57-
const options = { root: environmentSamples };
58-
const reporter = new InMemoryReporter();
59-
const defaultConfig = j(environmentSamples, 'cspell.default.json');
60-
61-
process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH] = defaultConfig;
62-
63-
const result = await runLint(new LintRequest(['**/*.tex'], options, reporter));
64-
expect(result.errors).toBe(0);
65-
expect(result.files).toBeGreaterThan(0);
66-
expect(result.issues).toBe(2);
52+
expect(result.issues).toBe(6);
6753
});
6854

6955
test('throws an error when the specified config file does not exist', async () => {
7056
const rootConfig = j(environmentSamples, 'nonexistent.json');
7157

72-
process.env[environmentKeys.CSPELL_CONFIG_PATH] = rootConfig
73-
process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH] = rootConfig
58+
process.env[environmentKeys.CSPELL_CONFIG_PATH] = rootConfig;
7459

7560
const readConfigResult = await readConfig(undefined, environmentSamples);
7661

@@ -90,22 +75,6 @@ describe('Linter Validation Tests', () => {
9075
expect(result.issues).toBe(0);
9176
});
9277

93-
test('handles malformed config file gracefully', async () => {
94-
const malformedPath = j(samples, 'cspell-bad.json');
95-
process.env[environmentKeys.CSPELL_CONFIG_PATH] = malformedPath;
96-
process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH] = malformedPath;
97-
98-
const result = await readConfig(undefined, samples);
99-
const resultConfig = result.config;
100-
101-
expect(result.source).toBe(malformedPath);
102-
expect(resultConfig).toHaveProperty('id', 'Invalid JSON');
103-
expect(resultConfig).toHaveProperty('name', 'Trailing comma');
104-
expect(resultConfig).toHaveProperty('__importRef');
105-
});
106-
107-
108-
10978
const optionsRootCSpellJson = { root, config: j(root, 'cspell.json') };
11079

11180
// cspell:ignore Tufte checkedd
@@ -162,7 +131,7 @@ describe('Linter Validation Tests', () => {
162131
const runResult = await runLint(new LintRequest(files, { ...options }, reporter));
163132
expect(runResult).toEqual(reporter.runResult);
164133
expect(stdout).toHaveBeenCalledOnce();
165-
expect(stdout.mock.calls.map(([data]) => data).join('')).toMatchFileSnapshot(
134+
await expect(stdout.mock.calls.map(([data]) => data).join('')).toMatchFileSnapshot(
166135
'./__snapshots__/logging/dictionary-logging.csv',
167136
);
168137
});

packages/cspell/src/app/lint/lint.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ import { CSpellReporterConfiguration } from '../models.js';
3131
import { npmPackage } from '../pkgInfo.js';
3232
import type { CreateCacheSettings, CSpellLintResultCache } from '../util/cache/index.js';
3333
import { calcCacheSettings, createCache } from '../util/cache/index.js';
34+
import { type ConfigInfo, readConfig } from '../util/configFileHelper.js';
3435
import { CheckFailed, toApplicationError, toError } from '../util/errors.js';
35-
import type { ConfigInfo, FileResult, ReadFileInfoResult } from '../util/fileHelper.js';
36+
import type { FileResult, ReadFileInfoResult } from '../util/fileHelper.js';
3637
import {
3738
fileInfoToDocument,
3839
filenameToUri,
3940
findFiles,
4041
isBinaryFile,
4142
isFile,
4243
isNotDir,
43-
readConfig,
4444
readFileInfo,
4545
readFileListFiles,
4646
resolveFilename,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as path from 'node:path';
2+
3+
import { searchForConfig } from 'cspell-lib';
4+
import { afterEach, describe, expect, test, vi } from 'vitest';
5+
6+
import { environmentKeys } from '../environment.js';
7+
import { pathPackageRoot } from '../test/test.helper.js';
8+
import { readConfig } from '../util/configFileHelper.js';
9+
10+
const root = pathPackageRoot;
11+
const samples = path.resolve(root, 'samples');
12+
const environmentSamples = path.resolve(samples, 'environment_config');
13+
14+
const j = path.join;
15+
16+
vi.mock('cspell-lib', async (importOriginal) => {
17+
const cspellLib = await importOriginal<typeof import('cspell-lib')>();
18+
19+
return {
20+
...cspellLib,
21+
searchForConfig: vi.fn(cspellLib.searchForConfig),
22+
};
23+
});
24+
25+
describe('Config File Helper', () => {
26+
afterEach(() => {
27+
delete process.env[environmentKeys.CSPELL_CONFIG_PATH];
28+
delete process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH];
29+
});
30+
31+
test('uses CSPELL_CONFIG_PATH as fallback when config not provided', async () => {
32+
const customConfig = j(environmentSamples, 'cspell.custom.json');
33+
process.env[environmentKeys.CSPELL_CONFIG_PATH] = customConfig;
34+
35+
const result = await readConfig(undefined, environmentSamples);
36+
37+
expect(result.source).toBe(customConfig);
38+
});
39+
40+
test('falls back to CSPELL_DEFAULT_CONFIG_PATH when no config is set', async () => {
41+
const defaultConfig = j(environmentSamples, 'cspell.default.json');
42+
43+
process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH] = defaultConfig;
44+
45+
vi.mocked(searchForConfig).mockImplementationOnce(async () => undefined);
46+
47+
const result = await readConfig(undefined, environmentSamples);
48+
49+
expect(result.source).toBe(defaultConfig);
50+
});
51+
52+
test('throws an error when the specified config file does not exist', async () => {
53+
const rootConfig = j(environmentSamples, 'nonexistent.json');
54+
55+
process.env[environmentKeys.CSPELL_CONFIG_PATH] = rootConfig;
56+
57+
const readConfigResult = await readConfig(undefined, environmentSamples);
58+
expect(readConfigResult.source).toBe(rootConfig);
59+
});
60+
61+
test('handles malformed config file gracefully', async () => {
62+
const malformedPath = j(samples, 'cspell-bad.json');
63+
process.env[environmentKeys.CSPELL_CONFIG_PATH] = malformedPath;
64+
process.env[environmentKeys.CSPELL_DEFAULT_CONFIG_PATH] = malformedPath;
65+
66+
const result = await readConfig(undefined, samples);
67+
const resultConfig = result.config;
68+
69+
expect(result.source).toBe(malformedPath);
70+
expect(resultConfig).toHaveProperty('id', 'Invalid JSON');
71+
expect(resultConfig).toHaveProperty('name', 'Trailing comma');
72+
expect(resultConfig).toHaveProperty('__importRef');
73+
});
74+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { toFilePathOrHref } from '@cspell/url';
2+
import type { CSpellUserSettings } from 'cspell-lib';
3+
import * as cspell from 'cspell-lib';
4+
5+
import { environmentKeys, getEnvironmentVariable } from '../environment.js';
6+
import { CSpellConfigFile } from '../options.js';
7+
import { filenameToUrl } from './fileHelper.js';
8+
9+
export interface ConfigInfo {
10+
source: string;
11+
config: CSpellUserSettings;
12+
}
13+
14+
export interface FileConfigInfo {
15+
configInfo: ConfigInfo;
16+
filename: string;
17+
text: string;
18+
languageIds: string[];
19+
}
20+
21+
export async function readConfig(
22+
configFile: string | CSpellConfigFile | undefined,
23+
root: string | undefined,
24+
): Promise<ConfigInfo> {
25+
configFile ??= getEnvironmentVariable(environmentKeys.CSPELL_CONFIG_PATH);
26+
27+
if (configFile) {
28+
const cfgFile = typeof configFile === 'string' ? await readConfigHandleError(configFile) : configFile;
29+
return configFileToConfigInfo(cfgFile);
30+
}
31+
const config = await cspell.searchForConfig(root);
32+
33+
const defaultConfigFile = getEnvironmentVariable(environmentKeys.CSPELL_DEFAULT_CONFIG_PATH);
34+
if (!config && defaultConfigFile) {
35+
const cfgFile = await readConfigFile(defaultConfigFile).catch(() => undefined);
36+
if (cfgFile) {
37+
return configFileToConfigInfo(cfgFile);
38+
}
39+
}
40+
41+
return { source: config?.__importRef?.filename || 'None found', config: config || {} };
42+
}
43+
44+
async function configFileToConfigInfo(cfgFile: CSpellConfigFile): Promise<ConfigInfo> {
45+
const config = await cspell.resolveConfigFileImports(cfgFile);
46+
const source = toFilePathOrHref(cfgFile.url);
47+
return { source, config };
48+
}
49+
50+
export function readConfigFile(filename: string | URL): Promise<CSpellConfigFile> {
51+
return cspell.readConfigFile(filename);
52+
}
53+
54+
async function readConfigHandleError(filename: string | URL): Promise<CSpellConfigFile> {
55+
try {
56+
return await readConfigFile(filename);
57+
} catch (e) {
58+
const settings: cspell.CSpellSettingsWithSourceTrace = {
59+
__importRef: {
60+
filename: filename.toString(),
61+
error: e as Error,
62+
},
63+
};
64+
return { url: filenameToUrl(filename), settings };
65+
}
66+
}

packages/cspell/src/app/util/fileHelper.ts

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import * as path from 'node:path';
33
import streamConsumers from 'node:stream/consumers';
44
import { fileURLToPath } from 'node:url';
55

6-
import { toFileDirURL, toFilePathOrHref, toFileURL } from '@cspell/url';
6+
import { toFileDirURL, toFileURL } from '@cspell/url';
77
import type { BufferEncoding } from 'cspell-io';
88
import { readFileText as cioReadFile, toURL } from 'cspell-io';
9-
import type { CSpellUserSettings, Document, Issue } from 'cspell-lib';
9+
import type { Document, Issue } from 'cspell-lib';
1010
import * as cspell from 'cspell-lib';
1111
import { fileToDocument, isBinaryFile as isUriBinaryFile } from 'cspell-lib';
1212

13-
import { environmentKeys, getEnvironmentVariable } from '../environment.js';
14-
import { CSpellConfigFile } from '../options.js';
1513
import { asyncAwait, asyncFlatten, asyncMap, asyncPipe, mergeAsyncIterables } from './async.js';
1614
import { FileUrlPrefix, STDIN, STDINProtocol, STDINUrlPrefix, UTF8 } from './constants.js';
1715
import { IOError, toApplicationError, toError } from './errors.js';
@@ -21,53 +19,6 @@ import { readStdin } from './stdin.js';
2119
import { isStdinUrl, resolveStdinUrl } from './stdinUrl.js';
2220
import { clean } from './util.js';
2321

24-
export interface ConfigInfo {
25-
source: string;
26-
config: CSpellUserSettings;
27-
}
28-
29-
export interface FileConfigInfo {
30-
configInfo: ConfigInfo;
31-
filename: string;
32-
text: string;
33-
languageIds: string[];
34-
}
35-
36-
export async function readConfig(
37-
configFile: string | CSpellConfigFile | undefined,
38-
root: string | undefined,
39-
): Promise<ConfigInfo> {
40-
configFile ??= getEnvironmentVariable(environmentKeys.CSPELL_CONFIG_PATH);
41-
configFile ??= getEnvironmentVariable(environmentKeys.CSPELL_DEFAULT_CONFIG_PATH);
42-
43-
if (configFile) {
44-
const cfgFile = typeof configFile === 'string' ? await readConfigHandleError(configFile) : configFile;
45-
const config = await cspell.resolveConfigFileImports(cfgFile);
46-
const source = toFilePathOrHref(cfgFile.url);
47-
return { source, config };
48-
}
49-
const config = await cspell.searchForConfig(root);
50-
return { source: config?.__importRef?.filename || 'None found', config: config || {} };
51-
}
52-
53-
export function readConfigFile(filename: string | URL): Promise<CSpellConfigFile> {
54-
return cspell.readConfigFile(filename);
55-
}
56-
57-
async function readConfigHandleError(filename: string | URL): Promise<CSpellConfigFile> {
58-
try {
59-
return await readConfigFile(filename);
60-
} catch (e) {
61-
const settings: cspell.CSpellSettingsWithSourceTrace = {
62-
__importRef: {
63-
filename: filename.toString(),
64-
error: e as Error,
65-
},
66-
};
67-
return { url: filenameToUrl(filename), settings };
68-
}
69-
}
70-
7122
export interface FileInfo {
7223
filename: string;
7324
text?: string;

0 commit comments

Comments
 (0)