cspell-lib 8.1.2 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/Settings/CSpellSettingsServer.d.ts +1 -1
- package/dist/esm/Settings/CSpellSettingsServer.js +1 -0
- package/dist/esm/Settings/Controller/configLoader/configLoader.d.ts +19 -8
- package/dist/esm/Settings/Controller/configLoader/configLoader.js +83 -49
- package/dist/esm/Settings/Controller/configLoader/configSearch.d.ts +7 -3
- package/dist/esm/Settings/Controller/configLoader/configSearch.js +68 -64
- package/dist/esm/Settings/Controller/configLoader/configToRawSettings.js +5 -2
- package/dist/esm/Settings/Controller/configLoader/normalizeRawSettings.d.ts +1 -1
- package/dist/esm/Settings/Controller/configLoader/normalizeRawSettings.js +6 -6
- package/dist/esm/Settings/DefaultSettings.js +5 -4
- package/dist/esm/Settings/DictionarySettings.js +4 -3
- package/dist/esm/Settings/GlobalSettings.js +5 -4
- package/dist/esm/Settings/mergeCache.d.ts +1 -0
- package/dist/esm/Settings/mergeCache.js +6 -1
- package/dist/esm/Settings/mergeList.d.ts +4 -0
- package/dist/esm/Settings/mergeList.js +6 -0
- package/dist/esm/SpellingDictionary/DictionaryLoader.js +1 -1
- package/dist/esm/fileSystem.d.ts +8 -0
- package/dist/esm/fileSystem.js +16 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/perf/perf.d.ts +11 -5
- package/dist/esm/perf/perf.js +16 -11
- package/dist/esm/util/AutoResolve.d.ts +12 -0
- package/dist/esm/util/AutoResolve.js +50 -1
- package/dist/esm/util/Uri.d.ts +10 -11
- package/dist/esm/util/Uri.js +5 -13
- package/dist/esm/util/findUp.d.ts +1 -1
- package/dist/esm/util/findUpFromUrl.d.ts +12 -0
- package/dist/esm/util/findUpFromUrl.js +44 -0
- package/dist/esm/util/resolveFile.d.ts +56 -35
- package/dist/esm/util/resolveFile.js +243 -147
- package/dist/esm/util/templates.d.ts +3 -0
- package/dist/esm/util/templates.js +36 -0
- package/dist/esm/util/url.d.ts +2 -1
- package/dist/esm/util/url.js +9 -3
- package/package.json +14 -14
- package/dist/esm/static.d.ts +0 -3
- package/dist/esm/static.js +0 -6
|
@@ -4,6 +4,7 @@ import type { OptionalOrUndefined } from '../util/types.js';
|
|
|
4
4
|
type CSpellSettingsWST = AdvancedCSpellSettingsWithSourceTrace;
|
|
5
5
|
export type CSpellSettingsWSTO = OptionalOrUndefined<AdvancedCSpellSettingsWithSourceTrace>;
|
|
6
6
|
export type CSpellSettingsI = CSpellSettingsInternal;
|
|
7
|
+
export { stats as getMergeStats } from './mergeList.js';
|
|
7
8
|
declare function mergeObjects(left: undefined, right: undefined): undefined;
|
|
8
9
|
declare function mergeObjects<T>(left: T, right: undefined): T;
|
|
9
10
|
declare function mergeObjects<T>(left: T, right: T): T;
|
|
@@ -35,5 +36,4 @@ export declare function extractDependencies(settings: CSpellSettingsWSTO | CSpel
|
|
|
35
36
|
export declare const __testing__: {
|
|
36
37
|
mergeObjects: typeof mergeObjects;
|
|
37
38
|
};
|
|
38
|
-
export {};
|
|
39
39
|
//# sourceMappingURL=CSpellSettingsServer.d.ts.map
|
|
@@ -9,6 +9,7 @@ import { configSettingsFileVersion0_1, ENV_CSPELL_GLOB_ROOT } from './constants.
|
|
|
9
9
|
import { calcDictionaryDefsToLoad, mapDictDefsToInternal } from './DictionarySettings.js';
|
|
10
10
|
import { mergeList, mergeListUnique } from './mergeList.js';
|
|
11
11
|
import { resolvePatterns } from './patterns.js';
|
|
12
|
+
export { stats as getMergeStats } from './mergeList.js';
|
|
12
13
|
const emptyWords = [];
|
|
13
14
|
Object.freeze(emptyWords);
|
|
14
15
|
const cachedMerges = new AutoResolveWeakCache();
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { CSpellUserSettings, ImportFileRef } from '@cspell/cspell-types';
|
|
2
2
|
import type { CSpellConfigFile, CSpellConfigFileReaderWriter } from 'cspell-config-lib';
|
|
3
|
-
import type {
|
|
3
|
+
import type { VFileSystem } from '../../../fileSystem.js';
|
|
4
4
|
import { AutoResolveCache } from '../../../util/AutoResolve.js';
|
|
5
|
+
import { FileResolver } from '../../../util/resolveFile.js';
|
|
5
6
|
import type { LoaderResult } from '../pnpLoader.js';
|
|
6
7
|
import { ConfigSearch } from './configSearch.js';
|
|
7
8
|
import { normalizeCacheSettings } from './normalizeRawSettings.js';
|
|
8
9
|
import type { PnPSettingsOptional } from './PnPSettings.js';
|
|
9
|
-
import type { CSpellSettingsI } from './types.js';
|
|
10
|
+
import type { CSpellSettingsI, CSpellSettingsWST } from './types.js';
|
|
10
11
|
export declare const sectionCSpell = "cSpell";
|
|
11
12
|
export declare const defaultFileName = "cspell.json";
|
|
12
13
|
interface ImportedConfigEntry {
|
|
@@ -69,15 +70,18 @@ export interface IConfigLoader {
|
|
|
69
70
|
* Unsubscribe from any events and dispose of any resources including caches.
|
|
70
71
|
*/
|
|
71
72
|
dispose(): void;
|
|
73
|
+
getStats(): Readonly<Record<string, Readonly<Record<string, number>>>>;
|
|
72
74
|
}
|
|
73
75
|
export declare class ConfigLoader implements IConfigLoader {
|
|
74
|
-
readonly
|
|
76
|
+
readonly fs: VFileSystem;
|
|
77
|
+
readonly templateVariables: Record<string, string>;
|
|
75
78
|
onReady: Promise<void>;
|
|
79
|
+
readonly fileResolver: FileResolver;
|
|
76
80
|
/**
|
|
77
81
|
* Use `createConfigLoader`
|
|
78
|
-
* @param
|
|
82
|
+
* @param virtualFs - virtual file system to use.
|
|
79
83
|
*/
|
|
80
|
-
protected constructor(
|
|
84
|
+
protected constructor(fs: VFileSystem, templateVariables?: Record<string, string>);
|
|
81
85
|
private subscribeToEvents;
|
|
82
86
|
protected cachedConfig: Map<string, ImportedConfigEntry>;
|
|
83
87
|
protected cachedConfigFiles: Map<string, CSpellConfigFile>;
|
|
@@ -113,22 +117,29 @@ export declare class ConfigLoader implements IConfigLoader {
|
|
|
113
117
|
* @param rawSettings - raw configuration settings
|
|
114
118
|
* @param pathToSettingsFile - path to the source file of the configuration settings.
|
|
115
119
|
*/
|
|
116
|
-
protected mergeImports(cfgFile: CSpellConfigFile, importedSettings: CSpellUserSettings[]): CSpellSettingsI
|
|
120
|
+
protected mergeImports(cfgFile: CSpellConfigFile, importedSettings: CSpellUserSettings[]): Promise<CSpellSettingsI>;
|
|
117
121
|
createCSpellConfigFile(filename: URL | string, settings: CSpellUserSettings): CSpellConfigFile;
|
|
118
122
|
dispose(): void;
|
|
123
|
+
getStats(): {
|
|
124
|
+
cacheMergeListUnique: Readonly<import("../../../util/AutoResolve.js").CacheStats>;
|
|
125
|
+
cacheMergeLists: Readonly<import("../../../util/AutoResolve.js").CacheStats>;
|
|
126
|
+
};
|
|
127
|
+
private resolveFilename;
|
|
119
128
|
}
|
|
120
129
|
declare class ConfigLoaderInternal extends ConfigLoader {
|
|
121
|
-
constructor(
|
|
130
|
+
constructor(vfs: VFileSystem);
|
|
122
131
|
get _cachedFiles(): Map<string, ImportedConfigEntry>;
|
|
123
132
|
}
|
|
124
133
|
export declare function loadPnP(pnpSettings: PnPSettingsOptional, searchFrom: URL): Promise<LoaderResult>;
|
|
134
|
+
declare function resolveGlobRoot(settings: CSpellSettingsWST, urlSettingsFile: URL): string;
|
|
125
135
|
declare function validateRawConfigVersion(config: CSpellConfigFile): void;
|
|
126
|
-
export declare function createConfigLoader(
|
|
136
|
+
export declare function createConfigLoader(fs?: VFileSystem): IConfigLoader;
|
|
127
137
|
export declare function getDefaultConfigLoaderInternal(): ConfigLoaderInternal;
|
|
128
138
|
export declare const __testing__: {
|
|
129
139
|
getDefaultConfigLoaderInternal: typeof getDefaultConfigLoaderInternal;
|
|
130
140
|
normalizeCacheSettings: typeof normalizeCacheSettings;
|
|
131
141
|
validateRawConfigVersion: typeof validateRawConfigVersion;
|
|
142
|
+
resolveGlobRoot: typeof resolveGlobRoot;
|
|
132
143
|
};
|
|
133
144
|
export {};
|
|
134
145
|
//# sourceMappingURL=configLoader.d.ts.map
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
2
|
import { createReaderWriter, CSpellConfigFileInMemory } from 'cspell-config-lib';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { isUrlLike, toFileURL } from 'cspell-io';
|
|
4
|
+
import path from 'path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
|
+
import { URI, Utils as UriUtils } from 'vscode-uri';
|
|
6
7
|
import { onClearCache } from '../../../events/index.js';
|
|
8
|
+
import { createTextFileResource, getVirtualFS } from '../../../fileSystem.js';
|
|
7
9
|
import { createCSpellSettingsInternal as csi } from '../../../Models/CSpellSettingsInternalDef.js';
|
|
8
10
|
import { AutoResolveCache } from '../../../util/AutoResolve.js';
|
|
9
11
|
import { logError, logWarning } from '../../../util/logger.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { FileResolver } from '../../../util/resolveFile.js';
|
|
13
|
+
import { envToTemplateVars } from '../../../util/templates.js';
|
|
14
|
+
import { addTrailingSlash, cwdURL, resolveFileWithURL, toFilePathOrHref, windowsDriveLetterToUpper, } from '../../../util/url.js';
|
|
12
15
|
import { configSettingsFileVersion0_1, configSettingsFileVersion0_2, currentSettingsFileVersion, ENV_CSPELL_GLOB_ROOT, } from '../../constants.js';
|
|
13
|
-
import { mergeSettings } from '../../CSpellSettingsServer.js';
|
|
16
|
+
import { getMergeStats, mergeSettings } from '../../CSpellSettingsServer.js';
|
|
14
17
|
import { getGlobalConfig } from '../../GlobalSettings.js';
|
|
15
18
|
import { ImportError } from '../ImportError.js';
|
|
16
19
|
import { pnpLoader } from '../pnpLoader.js';
|
|
@@ -26,15 +29,20 @@ export const sectionCSpell = 'cSpell';
|
|
|
26
29
|
export const defaultFileName = 'cspell.json';
|
|
27
30
|
let defaultConfigLoader = undefined;
|
|
28
31
|
export class ConfigLoader {
|
|
29
|
-
|
|
32
|
+
fs;
|
|
33
|
+
templateVariables;
|
|
30
34
|
onReady;
|
|
35
|
+
fileResolver;
|
|
31
36
|
/**
|
|
32
37
|
* Use `createConfigLoader`
|
|
33
|
-
* @param
|
|
38
|
+
* @param virtualFs - virtual file system to use.
|
|
34
39
|
*/
|
|
35
|
-
constructor(
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
40
|
+
constructor(fs, templateVariables = envToTemplateVars(process.env)) {
|
|
41
|
+
this.fs = fs;
|
|
42
|
+
this.templateVariables = templateVariables;
|
|
43
|
+
this.configSearch = new ConfigSearch(searchPlaces, fs);
|
|
44
|
+
this.cspellConfigFileReaderWriter = createReaderWriter(undefined, undefined, createIO(fs));
|
|
45
|
+
this.fileResolver = new FileResolver(fs, this.templateVariables);
|
|
38
46
|
this.onReady = this.prefetchGlobalSettingsAsync();
|
|
39
47
|
this.subscribeToEvents();
|
|
40
48
|
}
|
|
@@ -47,17 +55,17 @@ export class ConfigLoader {
|
|
|
47
55
|
cachedMergedConfig = new WeakMap();
|
|
48
56
|
globalSettings;
|
|
49
57
|
cspellConfigFileReaderWriter;
|
|
50
|
-
configSearch
|
|
58
|
+
configSearch;
|
|
51
59
|
toDispose = [];
|
|
52
60
|
async readSettingsAsync(filename, relativeTo, pnpSettings) {
|
|
53
61
|
await this.onReady;
|
|
54
|
-
const ref = resolveFilename(filename, relativeTo || pathToFileURL('./'));
|
|
62
|
+
const ref = await this.resolveFilename(filename, relativeTo || pathToFileURL('./'));
|
|
55
63
|
const entry = this.importSettings(ref, pnpSettings || defaultPnPSettings, []);
|
|
56
64
|
return entry.onReady;
|
|
57
65
|
}
|
|
58
66
|
async readConfigFile(filenameOrURL, relativeTo) {
|
|
59
|
-
const ref = resolveFilename(filenameOrURL.toString(), relativeTo || pathToFileURL('./'));
|
|
60
|
-
const url =
|
|
67
|
+
const ref = await this.resolveFilename(filenameOrURL.toString(), relativeTo || pathToFileURL('./'));
|
|
68
|
+
const url = toFileURL(ref.filename);
|
|
61
69
|
const href = url.href;
|
|
62
70
|
if (ref.error)
|
|
63
71
|
return new ImportError(`Failed to read config file: "${ref.filename}"`, ref.error);
|
|
@@ -80,8 +88,14 @@ export class ConfigLoader {
|
|
|
80
88
|
}
|
|
81
89
|
});
|
|
82
90
|
}
|
|
83
|
-
searchForConfigFileLocation(searchFrom) {
|
|
84
|
-
const url =
|
|
91
|
+
async searchForConfigFileLocation(searchFrom) {
|
|
92
|
+
const url = toFileURL(searchFrom || cwdURL(), cwdURL());
|
|
93
|
+
if (typeof searchFrom === 'string' && !isUrlLike(searchFrom) && url.protocol === 'file:') {
|
|
94
|
+
// check to see if it is a directory
|
|
95
|
+
if (await isDirectory(this.fs, url)) {
|
|
96
|
+
return this.configSearch.searchForConfig(addTrailingSlash(url));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
85
99
|
return this.configSearch.searchForConfig(url);
|
|
86
100
|
}
|
|
87
101
|
async searchForConfigFile(searchFrom) {
|
|
@@ -131,7 +145,7 @@ export class ConfigLoader {
|
|
|
131
145
|
return this.onReady;
|
|
132
146
|
}
|
|
133
147
|
importSettings(fileRef, pnpSettings, backReferences) {
|
|
134
|
-
const url =
|
|
148
|
+
const url = toFileURL(fileRef.filename);
|
|
135
149
|
const cacheKey = url.href;
|
|
136
150
|
const cachedImport = this.cachedConfig.get(cacheKey);
|
|
137
151
|
if (cachedImport) {
|
|
@@ -226,7 +240,7 @@ export class ConfigLoader {
|
|
|
226
240
|
const href = cfgFile.url.href;
|
|
227
241
|
const referencedSet = new Set(referencedBy);
|
|
228
242
|
const imports = normalizeImport(cfgFile.settings.import);
|
|
229
|
-
const __imports = imports.map((name) => resolveFilename(name, cfgFile.url));
|
|
243
|
+
const __imports = await Promise.all(imports.map((name) => this.resolveFilename(name, cfgFile.url)));
|
|
230
244
|
const toImport = __imports.map((ref) => this.importSettings(ref, pnpSettings, [...referencedBy, href]));
|
|
231
245
|
// Add ourselves to the import sources.
|
|
232
246
|
toImport.forEach((entry) => {
|
|
@@ -239,7 +253,7 @@ export class ConfigLoader {
|
|
|
239
253
|
: entry.onReady;
|
|
240
254
|
});
|
|
241
255
|
const importSettings = await Promise.all(pendingImports);
|
|
242
|
-
const cfg = this.mergeImports(cfgFile, importSettings);
|
|
256
|
+
const cfg = await this.mergeImports(cfgFile, importSettings);
|
|
243
257
|
return cfg;
|
|
244
258
|
}
|
|
245
259
|
/**
|
|
@@ -247,12 +261,11 @@ export class ConfigLoader {
|
|
|
247
261
|
* @param rawSettings - raw configuration settings
|
|
248
262
|
* @param pathToSettingsFile - path to the source file of the configuration settings.
|
|
249
263
|
*/
|
|
250
|
-
mergeImports(cfgFile, importedSettings) {
|
|
264
|
+
async mergeImports(cfgFile, importedSettings) {
|
|
251
265
|
const rawSettings = configToRawSettings(cfgFile);
|
|
252
266
|
const url = cfgFile.url;
|
|
253
267
|
const fileRef = rawSettings.__importRef;
|
|
254
268
|
const source = rawSettings.source;
|
|
255
|
-
assert(fileRef);
|
|
256
269
|
assert(source);
|
|
257
270
|
// Fix up dictionaryDefinitions
|
|
258
271
|
const settings = {
|
|
@@ -264,7 +277,7 @@ export class ConfigLoader {
|
|
|
264
277
|
const normalizedDictionaryDefs = normalizeDictionaryDefs(settings, url);
|
|
265
278
|
const normalizedSettingsGlobs = normalizeSettingsGlobs(settings, url);
|
|
266
279
|
const normalizedOverrides = normalizeOverrides(settings, url);
|
|
267
|
-
const normalizedReporters = normalizeReporters(settings, url);
|
|
280
|
+
const normalizedReporters = await normalizeReporters(settings, url);
|
|
268
281
|
const normalizedGitignoreRoot = normalizeGitignoreRoot(settings, url);
|
|
269
282
|
const normalizedCacheSettings = normalizeCacheSettings(settings, url);
|
|
270
283
|
const fileSettings = csi({
|
|
@@ -284,11 +297,13 @@ export class ConfigLoader {
|
|
|
284
297
|
const finalizeSettings = mergeSettings(mergedImportedSettings, fileSettings);
|
|
285
298
|
finalizeSettings.name = settings.name || finalizeSettings.name || '';
|
|
286
299
|
finalizeSettings.id = settings.id || finalizeSettings.id || '';
|
|
287
|
-
|
|
300
|
+
if (fileRef) {
|
|
301
|
+
finalizeSettings.__importRef = fileRef;
|
|
302
|
+
}
|
|
288
303
|
return finalizeSettings;
|
|
289
304
|
}
|
|
290
305
|
createCSpellConfigFile(filename, settings) {
|
|
291
|
-
return new CSpellConfigFileInMemory(
|
|
306
|
+
return new CSpellConfigFileInMemory(toFileURL(filename), settings);
|
|
292
307
|
}
|
|
293
308
|
dispose() {
|
|
294
309
|
while (this.toDispose.length) {
|
|
@@ -300,10 +315,25 @@ export class ConfigLoader {
|
|
|
300
315
|
}
|
|
301
316
|
}
|
|
302
317
|
}
|
|
318
|
+
getStats() {
|
|
319
|
+
return { ...getMergeStats() };
|
|
320
|
+
}
|
|
321
|
+
async resolveFilename(filename, relativeTo) {
|
|
322
|
+
if (filename instanceof URL)
|
|
323
|
+
return { filename: toFilePathOrHref(filename) };
|
|
324
|
+
const r = await this.fileResolver.resolveFile(filename, relativeTo);
|
|
325
|
+
if (r.warning) {
|
|
326
|
+
logWarning(r.warning);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
filename: r.filename.startsWith('file:/') ? fileURLToPath(r.filename) : r.filename,
|
|
330
|
+
error: r.found ? undefined : new Error(`Failed to resolve file: "${filename}"`),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
303
333
|
}
|
|
304
334
|
class ConfigLoaderInternal extends ConfigLoader {
|
|
305
|
-
constructor(
|
|
306
|
-
super(
|
|
335
|
+
constructor(vfs) {
|
|
336
|
+
super(vfs);
|
|
307
337
|
}
|
|
308
338
|
get _cachedFiles() {
|
|
309
339
|
return this.cachedConfig;
|
|
@@ -316,26 +346,17 @@ export function loadPnP(pnpSettings, searchFrom) {
|
|
|
316
346
|
const loader = pnpLoader(pnpSettings.pnpFiles);
|
|
317
347
|
return loader.load(searchFrom);
|
|
318
348
|
}
|
|
319
|
-
function resolveFilename(filename, relativeTo) {
|
|
320
|
-
if (filename instanceof URL)
|
|
321
|
-
return { filename: toFilePathOrHref(filename) };
|
|
322
|
-
const r = resolveFile(filename, relativeTo);
|
|
323
|
-
return {
|
|
324
|
-
filename: r.filename.startsWith('file:/') ? fileURLToPath(r.filename) : r.filename,
|
|
325
|
-
error: r.found ? undefined : new Error(`Failed to resolve file: "${filename}"`),
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
349
|
const nestedConfigDirectories = {
|
|
329
350
|
'.vscode': true,
|
|
330
|
-
'.config': true,
|
|
351
|
+
'.config': true, // this should be removed in the future, but it is a breaking change.
|
|
331
352
|
};
|
|
332
353
|
function resolveGlobRoot(settings, urlSettingsFile) {
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const settingsFileDirName =
|
|
354
|
+
const urlSettingsFileDir = new URL('.', urlSettingsFile);
|
|
355
|
+
const uriSettingsFileDir = URI.parse(urlSettingsFileDir.href);
|
|
356
|
+
const settingsFileDirName = UriUtils.basename(uriSettingsFileDir);
|
|
336
357
|
const isNestedConfig = settingsFileDirName in nestedConfigDirectories;
|
|
337
358
|
const isVSCode = settingsFileDirName === '.vscode';
|
|
338
|
-
const settingsFileDir = isNestedConfig ?
|
|
359
|
+
const settingsFileDir = (isNestedConfig ? UriUtils.dirname(uriSettingsFileDir) : uriSettingsFileDir).toString();
|
|
339
360
|
const envGlobRoot = process.env[ENV_CSPELL_GLOB_ROOT];
|
|
340
361
|
const defaultGlobRoot = envGlobRoot ?? '${cwd}';
|
|
341
362
|
const rawRoot = settings.globRoot ??
|
|
@@ -344,8 +365,12 @@ function resolveGlobRoot(settings, urlSettingsFile) {
|
|
|
344
365
|
(isVSCode && !settings.version)
|
|
345
366
|
? defaultGlobRoot
|
|
346
367
|
: settingsFileDir);
|
|
347
|
-
const globRoot = rawRoot.startsWith('${cwd}') ? rawRoot :
|
|
348
|
-
return globRoot
|
|
368
|
+
const globRoot = rawRoot.startsWith('${cwd}') ? rawRoot : resolveFileWithURL(rawRoot, new URL(settingsFileDir));
|
|
369
|
+
return typeof globRoot === 'string'
|
|
370
|
+
? globRoot
|
|
371
|
+
: globRoot.protocol === 'file:'
|
|
372
|
+
? windowsDriveLetterToUpper(path.resolve(fileURLToPath(globRoot)))
|
|
373
|
+
: addTrailingSlash(globRoot).href;
|
|
349
374
|
}
|
|
350
375
|
function validationMessage(msg, url) {
|
|
351
376
|
return msg + `\n File: "${toFilePathOrHref(url)}"`;
|
|
@@ -369,28 +394,37 @@ function validateRawConfigVersion(config) {
|
|
|
369
394
|
: `Legacy config file version found: "${version}", upgrade to "${currentSettingsFileVersion}"`;
|
|
370
395
|
logWarning(validationMessage(msg, config.url));
|
|
371
396
|
}
|
|
372
|
-
function createConfigLoaderInternal(
|
|
373
|
-
return new ConfigLoaderInternal(
|
|
397
|
+
function createConfigLoaderInternal(fs) {
|
|
398
|
+
return new ConfigLoaderInternal(fs ?? getVirtualFS().fs);
|
|
374
399
|
}
|
|
375
|
-
export function createConfigLoader(
|
|
376
|
-
return createConfigLoaderInternal(
|
|
400
|
+
export function createConfigLoader(fs) {
|
|
401
|
+
return createConfigLoaderInternal(fs);
|
|
377
402
|
}
|
|
378
403
|
export function getDefaultConfigLoaderInternal() {
|
|
379
404
|
if (defaultConfigLoader)
|
|
380
405
|
return defaultConfigLoader;
|
|
381
406
|
return (defaultConfigLoader = createConfigLoaderInternal());
|
|
382
407
|
}
|
|
383
|
-
function createIO(
|
|
384
|
-
const readFile = (url) =>
|
|
385
|
-
const writeFile = (file) =>
|
|
408
|
+
function createIO(fs) {
|
|
409
|
+
const readFile = (url) => fs.readFile(url).then((file) => ({ url: file.url, content: createTextFileResource(file).getText() }));
|
|
410
|
+
const writeFile = (file) => fs.writeFile(file);
|
|
386
411
|
return {
|
|
387
412
|
readFile,
|
|
388
413
|
writeFile,
|
|
389
414
|
};
|
|
390
415
|
}
|
|
416
|
+
async function isDirectory(fs, path) {
|
|
417
|
+
try {
|
|
418
|
+
return (await fs.stat(path)).isDirectory();
|
|
419
|
+
}
|
|
420
|
+
catch (e) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
391
424
|
export const __testing__ = {
|
|
392
425
|
getDefaultConfigLoaderInternal,
|
|
393
426
|
normalizeCacheSettings,
|
|
394
427
|
validateRawConfigVersion,
|
|
428
|
+
resolveGlobRoot,
|
|
395
429
|
};
|
|
396
430
|
//# sourceMappingURL=configLoader.js.map
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { type VFileSystem } from '../../../fileSystem.js';
|
|
1
2
|
export declare class ConfigSearch {
|
|
2
3
|
readonly searchPlaces: readonly string[];
|
|
4
|
+
private fs;
|
|
3
5
|
private searchCache;
|
|
4
6
|
private searchDirCache;
|
|
5
|
-
constructor(searchPlaces: readonly string[]);
|
|
6
|
-
searchForConfig(
|
|
7
|
+
constructor(searchPlaces: readonly string[], fs: VFileSystem);
|
|
8
|
+
searchForConfig(searchFromURL: URL): Promise<URL | undefined>;
|
|
7
9
|
clearCache(): void;
|
|
8
|
-
private findUpConfig;
|
|
9
10
|
private findUpConfigPath;
|
|
10
11
|
private hasConfig;
|
|
12
|
+
private createHasFileDirSearch;
|
|
13
|
+
private createHasFileStatCheck;
|
|
14
|
+
private hasConfigDir;
|
|
11
15
|
}
|
|
12
16
|
//# sourceMappingURL=configSearch.d.ts.map
|
|
@@ -1,35 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { pathToFileURL } from 'url';
|
|
1
|
+
import { urlBasename } from 'cspell-io';
|
|
2
|
+
import { createTextFileResource } from '../../../fileSystem.js';
|
|
4
3
|
import { createAutoResolveCache } from '../../../util/AutoResolve.js';
|
|
5
|
-
import {
|
|
6
|
-
import { addTrailingSlash, fileURLOrPathToPath, toFileDirUrl, toURL } from '../../../util/url.js';
|
|
4
|
+
import { findUpFromUrl } from '../../../util/findUpFromUrl.js';
|
|
7
5
|
export class ConfigSearch {
|
|
8
6
|
searchPlaces;
|
|
7
|
+
fs;
|
|
9
8
|
searchCache = new Map();
|
|
10
9
|
searchDirCache = new Map();
|
|
11
|
-
constructor(searchPlaces) {
|
|
10
|
+
constructor(searchPlaces, fs) {
|
|
12
11
|
this.searchPlaces = searchPlaces;
|
|
12
|
+
this.fs = fs;
|
|
13
13
|
this.searchPlaces = searchPlaces;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!searchFrom.startsWith('file:')) {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
if (searchFrom.protocol !== 'file:') {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const searchFromURL = toURL(searchFrom);
|
|
27
|
-
let dirUrl = new URL('.', searchFrom);
|
|
28
|
-
if (dirUrl.toString() !== searchFrom.toString()) {
|
|
29
|
-
// check to see if searchFrom is a directory
|
|
30
|
-
const isDir = await isDirectory(searchFrom);
|
|
31
|
-
dirUrl = isDir ? addTrailingSlash(searchFromURL) : dirUrl;
|
|
32
|
-
}
|
|
15
|
+
searchForConfig(searchFromURL) {
|
|
16
|
+
const dirUrl = new URL('.', searchFromURL);
|
|
33
17
|
const searchHref = dirUrl.href;
|
|
34
18
|
const searchCache = this.searchCache;
|
|
35
19
|
const cached = searchCache.get(searchHref);
|
|
@@ -37,80 +21,100 @@ export class ConfigSearch {
|
|
|
37
21
|
return cached;
|
|
38
22
|
}
|
|
39
23
|
const toPatchCache = [];
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const pFoundPath = Promise.resolve(foundPath);
|
|
43
|
-
const pFoundUrl = Promise.resolve(foundUrl);
|
|
24
|
+
const pFoundUrl = this.findUpConfigPath(dirUrl, storeVisit);
|
|
25
|
+
this.searchCache.set(searchHref, pFoundUrl);
|
|
44
26
|
const searchDirCache = this.searchDirCache;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
27
|
+
const patch = async () => {
|
|
28
|
+
try {
|
|
29
|
+
await pFoundUrl;
|
|
30
|
+
for (const dir of toPatchCache) {
|
|
31
|
+
searchDirCache.set(dir.href, searchDirCache.get(dir.href) || pFoundUrl);
|
|
32
|
+
searchCache.set(dir.href, searchCache.get(dir.href) || pFoundUrl);
|
|
33
|
+
}
|
|
34
|
+
const result = searchCache.get(searchHref) || pFoundUrl;
|
|
35
|
+
searchCache.set(searchHref, result);
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// ignore
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
patch();
|
|
42
|
+
return pFoundUrl;
|
|
53
43
|
function storeVisit(dir) {
|
|
54
44
|
toPatchCache.push(dir);
|
|
55
45
|
}
|
|
56
46
|
}
|
|
57
47
|
clearCache() {
|
|
58
48
|
this.searchCache.clear();
|
|
59
|
-
|
|
60
|
-
async findUpConfig(searchFromPath, visit) {
|
|
61
|
-
const cwd = fileURLOrPathToPath(searchFromPath);
|
|
62
|
-
const found = await this.findUpConfigPath(cwd, visit);
|
|
63
|
-
return found ? pathToFileURL(found) : undefined;
|
|
49
|
+
this.searchDirCache.clear();
|
|
64
50
|
}
|
|
65
51
|
findUpConfigPath(cwd, visit) {
|
|
66
52
|
const searchDirCache = this.searchDirCache;
|
|
67
|
-
const cached = searchDirCache.get(cwd);
|
|
53
|
+
const cached = searchDirCache.get(cwd.href);
|
|
68
54
|
if (cached)
|
|
69
55
|
return cached;
|
|
70
|
-
return
|
|
56
|
+
return findUpFromUrl((dir) => this.hasConfig(dir, visit), cwd, { type: 'file' });
|
|
71
57
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const cached = this.searchDirCache.get(dir);
|
|
58
|
+
hasConfig(dir, visited) {
|
|
59
|
+
const cached = this.searchDirCache.get(dir.href);
|
|
75
60
|
if (cached)
|
|
76
61
|
return cached;
|
|
77
62
|
visited(dir);
|
|
63
|
+
const result = this.hasConfigDir(dir);
|
|
64
|
+
this.searchDirCache.set(dir.href, result);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
createHasFileDirSearch() {
|
|
78
68
|
const dirInfoCache = createAutoResolveCache();
|
|
79
|
-
async
|
|
80
|
-
const
|
|
81
|
-
const
|
|
69
|
+
const hasFile = async (filename) => {
|
|
70
|
+
const dir = new URL('.', filename);
|
|
71
|
+
const dirUrlHref = dir.href;
|
|
72
|
+
const dirInfo = await dirInfoCache.get(dirUrlHref, async () => new Map((await this.fs.readDirectory(dir).catch(() => [])).map((ent) => [ent.name, ent])));
|
|
73
|
+
const name = urlBasename(filename);
|
|
82
74
|
const found = dirInfo.get(name);
|
|
83
75
|
return !!found?.isFile();
|
|
84
|
-
}
|
|
76
|
+
};
|
|
77
|
+
return hasFile;
|
|
78
|
+
}
|
|
79
|
+
createHasFileStatCheck() {
|
|
80
|
+
const hasFile = async (filename) => {
|
|
81
|
+
const stat = await this.fs.stat(filename).catch(() => undefined);
|
|
82
|
+
return !!stat?.isFile();
|
|
83
|
+
};
|
|
84
|
+
return hasFile;
|
|
85
|
+
}
|
|
86
|
+
async hasConfigDir(dir) {
|
|
87
|
+
const hasFile = this.fs.getCapabilities(dir).readDirectory
|
|
88
|
+
? this.createHasFileDirSearch()
|
|
89
|
+
: this.createHasFileStatCheck();
|
|
85
90
|
for (const searchPlace of this.searchPlaces) {
|
|
86
|
-
const file =
|
|
91
|
+
const file = new URL(searchPlace, dir);
|
|
87
92
|
const found = await hasFile(file);
|
|
88
93
|
if (found) {
|
|
89
|
-
if (
|
|
94
|
+
if (urlBasename(file) !== 'package.json')
|
|
90
95
|
return file;
|
|
91
|
-
if (await checkPackageJson(file))
|
|
96
|
+
if (await checkPackageJson(this.fs, file))
|
|
92
97
|
return file;
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
return undefined;
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
|
-
async function checkPackageJson(filename) {
|
|
103
|
+
async function checkPackageJson(fs, filename) {
|
|
99
104
|
try {
|
|
100
|
-
const
|
|
101
|
-
const pkg = JSON.parse(
|
|
105
|
+
const file = createTextFileResource(await fs.readFile(filename));
|
|
106
|
+
const pkg = JSON.parse(file.getText());
|
|
102
107
|
return typeof pkg.cspell === 'object';
|
|
103
108
|
}
|
|
104
109
|
catch (e) {
|
|
105
110
|
return false;
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
|
-
async function isDirectory(path) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
113
|
+
// async function isDirectory(virtualFs: VirtualFS, path: URL): Promise<boolean> {
|
|
114
|
+
// try {
|
|
115
|
+
// return (await virtualFs.fs.stat(path)).isDirectory();
|
|
116
|
+
// } catch (e) {
|
|
117
|
+
// return false;
|
|
118
|
+
// }
|
|
119
|
+
// }
|
|
116
120
|
//# sourceMappingURL=configSearch.js.map
|
|
@@ -17,13 +17,16 @@ export function configToRawSettings(cfgFile) {
|
|
|
17
17
|
};
|
|
18
18
|
const source = {
|
|
19
19
|
name: cfgFile.settings.name || filename,
|
|
20
|
-
filename,
|
|
20
|
+
filename: cfgFile.virtual ? undefined : filename,
|
|
21
21
|
};
|
|
22
22
|
const rawSettings = { ...cfgFile.settings };
|
|
23
23
|
rawSettings.import = normalizeImport(rawSettings.import);
|
|
24
24
|
normalizeRawConfig(rawSettings);
|
|
25
25
|
rawSettings.source = source;
|
|
26
|
-
|
|
26
|
+
// in virtual config files are ignored for the purposes of import history.
|
|
27
|
+
if (!cfgFile.virtual) {
|
|
28
|
+
rawSettings.__importRef = fileRef;
|
|
29
|
+
}
|
|
27
30
|
const id = rawSettings.id || urlToSimpleId(url);
|
|
28
31
|
const name = rawSettings.name || id;
|
|
29
32
|
rawSettings.id = id;
|
|
@@ -35,7 +35,7 @@ type NormalizeOverrides = Pick<CSpellUserSettings, 'globRoot' | 'overrides'>;
|
|
|
35
35
|
type NormalizeOverridesResult = Pick<CSpellUserSettings, 'overrides'>;
|
|
36
36
|
export declare function normalizeOverrides(settings: NormalizeOverrides, pathToSettingsFile: URL): NormalizeOverridesResult;
|
|
37
37
|
type NormalizeReporters = Pick<CSpellUserSettings, 'reporters'>;
|
|
38
|
-
export declare function normalizeReporters(settings: NormalizeReporters, pathToSettingsFile: URL): NormalizeReporters
|
|
38
|
+
export declare function normalizeReporters(settings: NormalizeReporters, pathToSettingsFile: URL): Promise<NormalizeReporters>;
|
|
39
39
|
export declare function normalizeLanguageSettings(languageSettings: LanguageSetting[] | undefined): LanguageSetting[] | undefined;
|
|
40
40
|
type NormalizeGitignoreRoot = Pick<CSpellUserSettings, 'gitignoreRoot'>;
|
|
41
41
|
export declare function normalizeGitignoreRoot(settings: NormalizeGitignoreRoot, pathToSettingsFile: URL): NormalizeGitignoreRoot;
|
|
@@ -38,20 +38,20 @@ export function normalizeOverrides(settings, pathToSettingsFile) {
|
|
|
38
38
|
});
|
|
39
39
|
return overrides ? { overrides } : {};
|
|
40
40
|
}
|
|
41
|
-
export function normalizeReporters(settings, pathToSettingsFile) {
|
|
41
|
+
export async function normalizeReporters(settings, pathToSettingsFile) {
|
|
42
42
|
if (settings.reporters === undefined)
|
|
43
43
|
return {};
|
|
44
|
-
function resolve(s) {
|
|
44
|
+
async function resolve(s) {
|
|
45
45
|
if (s === 'default')
|
|
46
46
|
return s;
|
|
47
|
-
const r = resolveFile(s, pathToSettingsFile);
|
|
47
|
+
const r = await resolveFile(s, pathToSettingsFile);
|
|
48
48
|
if (!r.found) {
|
|
49
49
|
// console.warn('Not found: %o', { filename: s, relativeTo: pathToSettingsFile.href });
|
|
50
50
|
throw new Error(`Not found: "${s}"`);
|
|
51
51
|
}
|
|
52
52
|
return r.filename;
|
|
53
53
|
}
|
|
54
|
-
function resolveReporter(s) {
|
|
54
|
+
async function resolveReporter(s) {
|
|
55
55
|
if (typeof s === 'string') {
|
|
56
56
|
return resolve(s);
|
|
57
57
|
}
|
|
@@ -59,10 +59,10 @@ export function normalizeReporters(settings, pathToSettingsFile) {
|
|
|
59
59
|
throw new Error('Invalid Reporter');
|
|
60
60
|
// Preserve the shape of Reporter Setting while resolving the reporter file.
|
|
61
61
|
const [r, ...rest] = s;
|
|
62
|
-
return [resolve(r), ...rest];
|
|
62
|
+
return [await resolve(r), ...rest];
|
|
63
63
|
}
|
|
64
64
|
return {
|
|
65
|
-
reporters: settings.reporters.map(resolveReporter),
|
|
65
|
+
reporters: await Promise.all(settings.reporters.map(resolveReporter)),
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
export function normalizeLanguageSettings(languageSettings) {
|