Skip to content

Commit 2cd9510

Browse files
authored
Fix support for the --config flag (#821)
1 parent 122487b commit 2cd9510

File tree

7 files changed

+343
-3
lines changed

7 files changed

+343
-3
lines changed

‎cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ const cli = meow(
5757
space: {
5858
type: 'string',
5959
},
60-
config: {
60+
configPath: {
6161
type: 'string',
62+
aliases: ['config'],
6263
},
6364
quiet: {
6465
type: 'boolean',
@@ -119,6 +120,7 @@ const linterOptions: LinterOptions = {
119120
cwd: (cliOptions.cwd && path.resolve(cliOptions.cwd)) ?? process.cwd(),
120121
quiet: cliOptions.quiet,
121122
ts: true,
123+
configPath: cliOptions.configPath,
122124
};
123125

124126
// Make data types for `options.space` match those of the API

‎lib/resolve-config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ export async function resolveXoConfig(options: LinterOptions): Promise<{
4646
let {
4747
config: flatOptions = [],
4848
filepath: flatConfigPath = '',
49-
} = await (flatConfigExplorer.search(searchPath) as Promise<{config: FlatXoConfig | undefined; filepath: string}>) ?? {};
49+
} = await (
50+
options.configPath
51+
? flatConfigExplorer.load(path.resolve(options.cwd, options.configPath)) as Promise<{config: FlatXoConfig | undefined; filepath: string}>
52+
: flatConfigExplorer.search(searchPath) as Promise<{config: FlatXoConfig | undefined; filepath: string}>
53+
) ?? {};
5054

5155
flatOptions = arrify(flatOptions);
5256

‎lib/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export type LinterOptions = {
6363
@private
6464
*/
6565
ts?: boolean;
66+
67+
/**
68+
Custom path to config to use for the linter.
69+
*/
70+
configPath?: string;
6671
};
6772

6873
export type LintTextOptions = {

‎lib/xo.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class Xo {
4242
filePath: options.filePath,
4343
quiet: options.quiet,
4444
ts: options.ts ?? true,
45+
configPath: options.configPath,
4546
},
4647
{
4748
react: options.react,
@@ -69,6 +70,7 @@ export class Xo {
6970
filePath: options.filePath,
7071
quiet: options.quiet,
7172
ts: options.ts,
73+
configPath: options.configPath,
7274
},
7375
{
7476
react: options.react,

‎package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@
133133
".history/**/*",
134134
"node_modules",
135135
"package.json",
136-
"xo.config.*"
136+
"xo.config.*",
137+
"**/*.ts"
137138
]
138139
}
139140
},

‎test/cli.test.ts

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,3 +993,168 @@ test('ts rules apply to js files when "files" is set to a relative glob path', a
993993
t.true(cachedTsConfig.files?.includes(tsFilePath), 'TypeScript file should be included in cached tsconfig');
994994
t.true(cachedTsConfig.files?.includes(jsFilePath), 'JavaScript file should be included in cached tsconfig');
995995
});
996+
997+
// Supports a custom config file
998+
test('supports a custom config file with absolute path', async t => {
999+
const {cwd} = t.context;
1000+
1001+
// Create a simple JS file
1002+
const filePath = path.join(cwd, 'test.js');
1003+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1004+
1005+
// Create a custom XO config file
1006+
const customConfigPath = path.join(cwd, 'custom.xo.config.js');
1007+
const customConfig = dedent`
1008+
export default [
1009+
{ ignores: "xo.config.js" },
1010+
{
1011+
rules: {
1012+
'no-console': 'error',
1013+
}
1014+
}
1015+
]
1016+
`;
1017+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1018+
1019+
// Run XO with the custom config file
1020+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ${customConfigPath}`);
1021+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1022+
t.true((error.stdout as string)?.includes('no-console'), 'The specific rule should be mentioned in the output');
1023+
});
1024+
1025+
test('supports a custom config file with relative path', async t => {
1026+
const {cwd} = t.context;
1027+
1028+
// Create a simple JS file
1029+
const filePath = path.join(cwd, 'test.js');
1030+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1031+
1032+
// Create a custom XO config file
1033+
const customConfigPath = path.join(cwd, 'custom.xo.config.js');
1034+
const customConfig = dedent`
1035+
export default [
1036+
{ ignores: "xo.config.js" },
1037+
{
1038+
rules: {
1039+
'no-console': 'error',
1040+
}
1041+
}
1042+
]
1043+
`;
1044+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1045+
1046+
// Run XO with the custom config file with relative path
1047+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ${path.relative(cwd, customConfigPath)}`);
1048+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1049+
t.true((error.stdout as string)?.includes('no-console'), 'The specific rule should be mentioned in the output');
1050+
});
1051+
1052+
test('supports a custom config file with relative dot slash path', async t => {
1053+
const {cwd} = t.context;
1054+
1055+
// Create a simple JS file
1056+
const filePath = path.join(cwd, 'test.js');
1057+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1058+
1059+
// Create a custom XO config file
1060+
const customConfigPath = path.join(cwd, 'custom.xo.config.js');
1061+
const customConfig = dedent`
1062+
export default [
1063+
{ ignores: "xo.config.js" },
1064+
{
1065+
rules: {
1066+
'no-console': 'error',
1067+
}
1068+
}
1069+
]
1070+
`;
1071+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1072+
1073+
// Run XO with the custom config file with relative path
1074+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ./${path.relative(cwd, customConfigPath)}`);
1075+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1076+
t.true((error.stdout as string)?.includes('no-console'), 'The specific rule should be mentioned in the output');
1077+
});
1078+
1079+
// Supports custom config file with ts path
1080+
test('supports a custom config file with absolute path for TypeScript', async t => {
1081+
const {cwd} = t.context;
1082+
1083+
// Create a simple TS file
1084+
const filePath = path.join(cwd, 'test.js');
1085+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1086+
1087+
// Create a custom XO config file
1088+
const customConfigPath = path.join(cwd, 'custom.xo.config.ts');
1089+
const customConfig = dedent`
1090+
export default [
1091+
{ ignores: "custom.xo.config.ts" },
1092+
{
1093+
rules: {
1094+
'no-console': 'error',
1095+
}
1096+
}
1097+
]
1098+
`;
1099+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1100+
1101+
// Run XO with the custom config file
1102+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ${customConfigPath}`);
1103+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1104+
t.true((error.stdout as string)?.includes('no-console'), 'The specific TypeScript rule should be mentioned in the output');
1105+
});
1106+
1107+
// Supports custom config file with ts path
1108+
test('supports a custom config file with relative path for TypeScript', async t => {
1109+
const {cwd} = t.context;
1110+
1111+
// Create a simple TS file
1112+
const filePath = path.join(cwd, 'test.js');
1113+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1114+
1115+
// Create a custom XO config file
1116+
const customConfigPath = path.join(cwd, 'custom.xo.config.ts');
1117+
const customConfig = dedent`
1118+
export default [
1119+
{ ignores: "custom.xo.config.ts" },
1120+
{
1121+
rules: {
1122+
'no-console': 'error',
1123+
}
1124+
}
1125+
]
1126+
`;
1127+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1128+
1129+
// Run XO with the custom config file
1130+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ${path.relative(cwd, customConfigPath)}`);
1131+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1132+
t.true((error.stdout as string)?.includes('no-console'), 'The specific TypeScript rule should be mentioned in the output');
1133+
});
1134+
// Supports custom config file with ts path
1135+
test('supports a custom config file with relative dot slash path for TypeScript', async t => {
1136+
const {cwd} = t.context;
1137+
1138+
// Create a simple TS file
1139+
const filePath = path.join(cwd, 'test.js');
1140+
await fs.writeFile(filePath, dedent`console.log('hello');\n`, 'utf8');
1141+
1142+
// Create a custom XO config file
1143+
const customConfigPath = path.join(cwd, 'custom.xo.config.ts');
1144+
const customConfig = dedent`
1145+
export default [
1146+
{ ignores: "custom.xo.config.ts" },
1147+
{
1148+
rules: {
1149+
'no-console': 'error',
1150+
}
1151+
}
1152+
]
1153+
`;
1154+
await fs.writeFile(customConfigPath, customConfig, 'utf8');
1155+
1156+
// Run XO with the custom config file
1157+
const error = await t.throwsAsync<ExecaError>($`node ./dist/cli --cwd ${cwd} --config ./${path.relative(cwd, customConfigPath)}`);
1158+
t.true((error.stdout as string)?.includes('test.js'), 'Error should be reported for the test.js file');
1159+
t.true((error.stdout as string)?.includes('no-console'), 'The specific TypeScript rule should be mentioned in the output');
1160+
});

0 commit comments

Comments
 (0)