Skip to content

Commit adbecc5

Browse files
authored
feat: add Rslint as optional tool (#126)
1 parent e22a6f1 commit adbecc5

9 files changed

Lines changed: 131 additions & 47 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
22
"search.useIgnoreFiles": true,
3+
"cSpell.words": [
4+
"rslint"
5+
],
36
}

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"files": [
2020
"template-biome",
2121
"template-eslint",
22+
"template-rslint",
2223
"template-prettier",
2324
"dist"
2425
],
@@ -35,14 +36,15 @@
3536
},
3637
"nano-staged": {
3738
"*.{js,jsx,ts,tsx,mjs,cjs}": [
38-
"rslint && prettier --write"
39+
"rslint",
40+
"prettier --write"
3941
]
4042
},
4143
"devDependencies": {
4244
"@clack/prompts": "^1.2.0",
4345
"@microsoft/api-extractor": "^7.58.2",
4446
"@rslib/core": "0.21.0",
45-
"@rslint/core": "^0.4.1",
47+
"@rslint/core": "^0.4.2",
4648
"@rstest/core": "0.9.7",
4749
"@types/fs-extra": "^11.0.4",
4850
"@types/minimist": "^1.2.5",

pnpm-lock.yaml

Lines changed: 30 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export type Argv = {
115115
'package-name'?: string;
116116
};
117117

118-
export const BUILTIN_TOOLS = ['biome', 'eslint', 'prettier'];
118+
export const BUILTIN_TOOLS = ['eslint', 'rslint', 'biome', 'prettier'];
119119

120120
function logHelpMessage(
121121
name: string,
@@ -200,9 +200,10 @@ async function getTools(
200200
}
201201

202202
const options = [
203-
{ value: 'biome', label: 'Biome - linting & formatting' },
204203
{ value: 'eslint', label: 'ESLint - linting' },
204+
{ value: 'rslint', label: 'Rslint - linting (experimental)' },
205205
{ value: 'prettier', label: 'Prettier - formatting' },
206+
{ value: 'biome', label: 'Biome - linting & formatting' },
206207
];
207208

208209
if (filteredExtraTools) {
@@ -253,7 +254,9 @@ function orderExtraSkills(extraSkills: ExtraSkill[] | undefined) {
253254

254255
return [
255256
...extraSkills.filter((extraSkill) => extraSkill.order === 'pre'),
256-
...extraSkills.filter((extraSkill) => typeof extraSkill.order === 'undefined'),
257+
...extraSkills.filter(
258+
(extraSkill) => typeof extraSkill.order === 'undefined',
259+
),
257260
...extraSkills.filter((extraSkill) => extraSkill.order === 'post'),
258261
];
259262
}
@@ -266,7 +269,11 @@ async function getSkills(
266269
promptMultiselect: typeof multiselect = multiselect,
267270
) {
268271
const parsedSkills = parseSkillsOption(skill);
269-
const filteredExtraSkills = filterExtraSkills(extraSkills, templateName, tools);
272+
const filteredExtraSkills = filterExtraSkills(
273+
extraSkills,
274+
templateName,
275+
tools,
276+
);
270277

271278
if (parsedSkills !== null) {
272279
// Treat explicit `--skill` values as authoritative as long as they refer to
@@ -288,7 +295,8 @@ async function getSkills(
288295

289296
return checkCancel<string[]>(
290297
await promptMultiselect({
291-
message: 'Select optional skills (Use <space> to select, <enter> to continue)',
298+
message:
299+
'Select optional skills (Use <space> to select, <enter> to continue)',
292300
options: orderedExtraSkills.map((extraSkill) => ({
293301
value: extraSkill.value,
294302
label: extraSkill.label,
@@ -384,7 +392,11 @@ type ExtraSkill = {
384392
order?: 'pre' | 'post';
385393
};
386394

387-
async function runCommand(command: string, cwd: string, packageManager: string) {
395+
async function runCommand(
396+
command: string,
397+
cwd: string,
398+
packageManager: string,
399+
) {
388400
// Replace `npm create` with the equivalent command for the detected package manager
389401
if (command.startsWith('npm create ')) {
390402
const createReplacements: Record<string, string> = {
@@ -415,17 +427,17 @@ async function runCommand(command: string, cwd: string, packageManager: string)
415427
});
416428

417429
if (result.exitCode !== 0) {
418-
const details = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
430+
const details = [result.stderr, result.stdout]
431+
.filter(Boolean)
432+
.join('\n')
433+
.trim();
419434
throw new Error(
420435
`Failed to run command: ${command}${details ? `\n${details}` : ''}`,
421436
);
422437
}
423438
}
424439

425-
async function runSkillCommand(
426-
skills: ExtraSkill[],
427-
cwd: string,
428-
) {
440+
async function runSkillCommand(skills: ExtraSkill[], cwd: string) {
429441
const [firstSkill] = skills;
430442
// `skills add` accepts repeated `--skill` flags for a single source.
431443
const installArgs = skills.flatMap((skill) => [
@@ -465,7 +477,9 @@ async function runSkillCommand(
465477
const result = await proc;
466478

467479
if (result.exitCode !== 0) {
468-
const quotedSkillLabel = skills.map((skill) => `"${skill.value}"`).join(', ');
480+
const quotedSkillLabel = skills
481+
.map((skill) => `"${skill.value}"`)
482+
.join(', ');
469483
const message = `Failed to install ${skillNoun} ${quotedSkillLabel} from "${firstSkill.source}" using command: ${command}`;
470484
installationTaskLog.error(message);
471485
throw new Error(message);
@@ -579,7 +593,13 @@ export async function create({
579593

580594
const templateName = await getTemplateName(argv);
581595
const tools = await getTools(argv, extraTools, templateName);
582-
const skills = await getSkills(argv, extraSkills, templateName, tools, multiselect);
596+
const skills = await getSkills(
597+
argv,
598+
extraSkills,
599+
templateName,
600+
tools,
601+
multiselect,
602+
);
583603

584604
const srcFolder = path.join(root, `template-${templateName}`);
585605
const commonFolder = path.join(root, 'template-common');

template-rslint/AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Tools
2+
3+
### Rslint
4+
5+
- Run `{{ packageManager }} run lint` to lint your code

template-rslint/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "rslint",
3+
"private": true,
4+
"version": "1.0.0",
5+
"scripts": {
6+
"lint": "rslint"
7+
},
8+
"devDependencies": {
9+
"@rslint/core": "^0.4.2"
10+
}
11+
}

template-rslint/rslint.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { defineConfig, ts } from '@rslint/core';
2+
3+
export default defineConfig([ts.configs.recommended]);

test/cli.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,38 @@ test('should accept comma separated tools option', async () => {
4747
expect(fs.existsSync(path.join(projectDir, '.prettierrc'))).toBe(true);
4848
});
4949

50+
test('should scaffold rslint tool files', async () => {
51+
const projectDir = path.join(testDir, 'rslint-tool');
52+
53+
await create({
54+
name: 'test',
55+
root: fixturesDir,
56+
templates: ['vanilla'],
57+
getTemplateName: async () => 'vanilla',
58+
argv: [
59+
'node',
60+
'test',
61+
'--dir',
62+
projectDir,
63+
'--template',
64+
'vanilla',
65+
'--tools',
66+
'rslint',
67+
],
68+
});
69+
70+
expect(fs.existsSync(path.join(projectDir, 'rslint.config.ts'))).toBe(true);
71+
72+
const packageJson = JSON.parse(
73+
fs.readFileSync(path.join(projectDir, 'package.json'), 'utf-8'),
74+
);
75+
76+
expect(packageJson.scripts).toMatchObject({
77+
lint: 'rslint',
78+
});
79+
expect(packageJson.devDependencies['@rslint/core']).toBeTruthy();
80+
});
81+
5082
test('should skip tools selection', async () => {
5183
const projectDir = path.join(testDir, 'comma-separated-tools');
5284

test/help.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ test('help message includes extra tools', async () => {
2828
}
2929

3030
const logOutput = logs.join('\n');
31-
expect(logOutput).toContain('biome, eslint, prettier, custom-tool');
31+
expect(logOutput).toContain('eslint, rslint, biome, prettier, custom-tool');
3232
});
3333

3434
test('help message hides skill help when no optional skills are configured', async () => {
@@ -133,7 +133,15 @@ test('help message lists all optional skills even when template and tools are pr
133133
when: ({ tools }) => tools.includes('rstest'),
134134
},
135135
],
136-
argv: ['node', 'test', '--help', '--template', 'vanilla', '--tools', 'biome'],
136+
argv: [
137+
'node',
138+
'test',
139+
'--help',
140+
'--template',
141+
'vanilla',
142+
'--tools',
143+
'biome',
144+
],
137145
});
138146
} finally {
139147
logger.override({

0 commit comments

Comments
 (0)