Skip to content

Commit 72f1712

Browse files
authored
feat: Support loading .ts and .mts config files. (#7961)
1 parent de82233 commit 72f1712

File tree

13 files changed

+140
-21
lines changed

13 files changed

+140
-21
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const config = {
2+
version: '0.2',
3+
id: 'module/mts',
4+
};
5+
6+
export default config;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const config = {
2+
version: '0.2',
3+
id: 'module/ts',
4+
};
5+
6+
export default config;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}

packages/cspell-config-lib/src/loaders/loaderJavaScript.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { loaderJavaScript } from './loaderJavaScript.js';
1010
const oc = (...params: Parameters<typeof expect.objectContaining>) => expect.objectContaining(...params);
1111
const ac = (...params: Parameters<typeof expect.arrayContaining>) => expect.arrayContaining(...params);
1212

13+
const supportsTypeScriptConfig = process.version >= 'v22';
14+
1315
describe('loaderJavaScript', () => {
1416
afterEach(() => {});
1517

@@ -47,6 +49,37 @@ describe('loaderJavaScript', () => {
4749
expect(result4.settings).not.toBe(result.settings);
4850
});
4951

52+
test.skipIf(!supportsTypeScriptConfig).each`
53+
file | expected
54+
${'ts/cspell.config.ts'} | ${{ settings: oc({ id: 'module/ts' }) }}
55+
${'ts/cspell.config.mts'} | ${{ settings: oc({ id: 'module/mts' }) }}
56+
`('loaderJavaScript $file', async ({ file, expected }) => {
57+
const url = pathToFileURL(fixtures(file));
58+
expected.url ??= url;
59+
const next = vi.fn();
60+
61+
const result = await loaderJavaScript.load({ url, context: { deserialize, io: defaultIO } }, next);
62+
expect(result).toEqual(expected);
63+
64+
// Try double loading.
65+
const result2 = await loaderJavaScript.load({ url, context: { deserialize, io: defaultIO } }, next);
66+
expect(result2.settings).toBe(result.settings);
67+
68+
// Ensure that we can force a load by changing search params.
69+
const url3 = new URL(url.href);
70+
url3.searchParams.append('q', '29');
71+
72+
const result3 = await loaderJavaScript.load({ url: url3, context: { deserialize, io: defaultIO } }, next);
73+
expect(result3.settings).not.toBe(result.settings);
74+
expect(result3.settings).toEqual(result.settings);
75+
76+
// Ensure that we can force a load by changing the hash.
77+
const url4 = new URL(url.href);
78+
url4.hash = 'hash';
79+
const result4 = await loaderJavaScript.load({ url: url4, context: { deserialize, io: defaultIO } }, next);
80+
expect(result4.settings).not.toBe(result.settings);
81+
});
82+
5083
/* cspell:ignore lazr */
5184

5285
test.each`

packages/cspell-config-lib/src/loaders/loaderJavaScript.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ async function importJavaScript(url: URL, hashSuffix: number | string): Promise<
1414
const _url = new URL(url.href);
1515
_url.hash = `${_url.hash};loaderSuffix=${hashSuffix}`;
1616
_log('importJavaScript: %o', { url: _url.href });
17-
const result = await import(_url.href);
18-
const settingsOrFunction = await (result.default ?? result);
17+
let result = await import(_url.href);
18+
result = result.default ?? result;
19+
// It is possible to have a nested `default` when using a TypeScript loader.
20+
result = result.default ?? result;
21+
const settingsOrFunction = await result;
1922
const settings = typeof settingsOrFunction === 'function' ? await settingsOrFunction() : settingsOrFunction;
2023
return new CSpellConfigFileJavaScript(url, settings);
2124
} catch (e) {
@@ -36,7 +39,10 @@ export class LoaderJavaScript implements FileLoaderMiddleware {
3639
switch (ext) {
3740
case '.js':
3841
case '.cjs':
39-
case '.mjs': {
42+
case '.mjs':
43+
case '.ts':
44+
case '.cts':
45+
case '.mts': {
4046
return importJavaScript(url, this.hashSuffix);
4147
}
4248
}

packages/cspell-lib/src/lib/Settings/Controller/configLoader/configLocations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const supportedExtensions = ['.json', '.jsonc', '.yaml', '.yml', '.mjs', '.cjs', '.js', '.toml'];
1+
const supportedExtensions = ['.json', '.jsonc', '.yaml', '.yml', '.mjs', '.cjs', '.js', '.toml', '.mts', '.ts'];
22

33
/**
44
* Logic of the locations:

packages/cspell-lib/src/lib/__snapshots__/index.test.ts.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,14 @@ exports[`Validate the cspell API > Verify API exports 1`] = `
185185
"cspell.config.cjs",
186186
"cspell.config.js",
187187
"cspell.config.toml",
188+
"cspell.config.mts",
189+
"cspell.config.ts",
188190
".cspell.config.mjs",
189191
".cspell.config.cjs",
190192
".cspell.config.js",
191193
".cspell.config.toml",
194+
".cspell.config.mts",
195+
".cspell.config.ts",
192196
".cspell.yaml",
193197
".cspell.yml",
194198
"cspell.yaml",
@@ -207,6 +211,8 @@ exports[`Validate the cspell API > Verify API exports 1`] = `
207211
".config/cspell.config.cjs",
208212
".config/cspell.config.js",
209213
".config/cspell.config.toml",
214+
".config/cspell.config.mts",
215+
".config/cspell.config.ts",
210216
".config/.cspell.config.json",
211217
".config/.cspell.config.jsonc",
212218
".config/.cspell.config.yaml",
@@ -215,6 +221,8 @@ exports[`Validate the cspell API > Verify API exports 1`] = `
215221
".config/.cspell.config.cjs",
216222
".config/.cspell.config.js",
217223
".config/.cspell.config.toml",
224+
".config/.cspell.config.mts",
225+
".config/.cspell.config.ts",
218226
".config/cspell.yaml",
219227
".config/cspell.yml",
220228
],
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// cspell:words typescriptconfig
2+
3+
const config = {
4+
version: '0.2',
5+
id: 'module/ts',
6+
words: ['typescriptconfig'],
7+
dictionaries: ['npm'],
8+
};
9+
10+
export default config;

packages/cspell/src/__snapshots__/app.test.ts.snap

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ exports[`Validate cli > app 'LICENSE' Expect Error: undefined 3`] = `""`;
175175
exports[`Validate cli > app 'bad config' Expect Error: [Function CheckFailed] 1`] = `[]`;
176176

177177
exports[`Validate cli > app 'bad config' Expect Error: [Function CheckFailed] 2`] = `
178-
"error Configuration Error: Failed to read config file: "./src/app.test.ts"
178+
"error Configuration Error: Failed to read config file: "./src/app.test.ts.x"
179179
error
180180
error -------------------------------------------
181181
error
@@ -4892,3 +4892,12 @@ hello - web-services* node_modules/@cspell/…/dict/webServices.txt
48924892
hello - workspace* ../../cspell-dict.txt
48934893
"
48944894
`;
4895+
4896+
exports[`Validate cli > app trace 'trace registered' run with 'trace typescriptconfig --config fixtures/features/ts-config/cspell.config.ts …' 1`] = `[]`;
4897+
4898+
exports[`Validate cli > app trace 'trace registered' run with 'trace typescriptconfig --config fixtures/features/ts-config/cspell.config.ts …' 2`] = `
4899+
"Word F Dictionary Dictionary Location
4900+
typescriptconfig * [words]* fixtures/features/ts-config/cspell.config.ts
4901+
typescriptconfig * cpp node_modules/@cspell/dict-cpp/dict/cpp.txt.gz
4902+
"
4903+
`;

packages/cspell/src/app.mts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Command } from 'commander';
2-
import { Option as CommanderOption, program } from 'commander';
2+
import { program } from 'commander';
33
import { satisfies as semverSatisfies } from 'semver';
44

55
import { commandCheck } from './commandCheck.js';
@@ -9,6 +9,7 @@ import { commandLink } from './commandLink.js';
99
import { commandLint } from './commandLint.js';
1010
import { commandSuggestion } from './commandSuggestion.js';
1111
import { commandTrace } from './commandTrace.js';
12+
import { addGlobalOptionsAndHooks, addGlobalOptionsToAction } from './globalOptions.js';
1213
import { npmPackage } from './pkgInfo.js';
1314
import { ApplicationError } from './util/errors.js';
1415

@@ -29,17 +30,15 @@ export async function run(command?: Command, argv?: string[]): Promise<void> {
2930
);
3031
}
3132

32-
const optionFlags = new CommanderOption('-f,--flag <flag:value>', 'Declare an execution flag value')
33-
.hideHelp()
34-
.argParser((value: string, prev: undefined | string[]) => prev?.concat(value) || [value]);
35-
36-
commandLint(prog).addOption(optionFlags);
37-
commandTrace(prog).addOption(optionFlags);
38-
commandCheck(prog).addOption(optionFlags);
39-
commandSuggestion(prog).addOption(optionFlags);
40-
commandInit(prog).addOption(optionFlags);
33+
addGlobalOptionsToAction(commandLint(prog, { isDefault: true }));
34+
addGlobalOptionsToAction(commandTrace(prog));
35+
addGlobalOptionsToAction(commandCheck(prog));
36+
addGlobalOptionsToAction(commandSuggestion(prog));
37+
addGlobalOptionsToAction(commandInit(prog));
4138
commandLink(prog);
42-
commandDictionaries(prog);
39+
addGlobalOptionsToAction(commandDictionaries(prog));
40+
41+
addGlobalOptionsAndHooks(prog);
4342

4443
prog.exitOverride();
4544
await prog.parseAsync(args);

0 commit comments

Comments
 (0)