Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"search.useIgnoreFiles": true,
"cSpell.words": [
"rslint"
],
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
},
"nano-staged": {
"*.{js,jsx,ts,tsx,mjs,cjs}": [
"rslint && prettier --write"
"rslint",
"prettier --write"
]
},
"devDependencies": {
Expand Down
46 changes: 33 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export type Argv = {
'package-name'?: string;
};

export const BUILTIN_TOOLS = ['biome', 'eslint', 'prettier'];
export const BUILTIN_TOOLS = ['eslint', 'rslint', 'biome', 'prettier'];
Comment thread
chenjiahan marked this conversation as resolved.

Comment thread
chenjiahan marked this conversation as resolved.
function logHelpMessage(
name: string,
Expand Down Expand Up @@ -200,9 +200,10 @@ async function getTools(
}

const options = [
{ value: 'biome', label: 'Biome - linting & formatting' },
{ value: 'eslint', label: 'ESLint - linting' },
{ value: 'rslint', label: 'Rslint - linting (experimental)' },
{ value: 'prettier', label: 'Prettier - formatting' },
{ value: 'biome', label: 'Biome - linting & formatting' },
];
Comment thread
chenjiahan marked this conversation as resolved.

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

return [
...extraSkills.filter((extraSkill) => extraSkill.order === 'pre'),
...extraSkills.filter((extraSkill) => typeof extraSkill.order === 'undefined'),
...extraSkills.filter(
(extraSkill) => typeof extraSkill.order === 'undefined',
),
...extraSkills.filter((extraSkill) => extraSkill.order === 'post'),
];
}
Expand All @@ -266,7 +269,11 @@ async function getSkills(
promptMultiselect: typeof multiselect = multiselect,
) {
const parsedSkills = parseSkillsOption(skill);
const filteredExtraSkills = filterExtraSkills(extraSkills, templateName, tools);
const filteredExtraSkills = filterExtraSkills(
extraSkills,
templateName,
tools,
);

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

return checkCancel<string[]>(
await promptMultiselect({
message: 'Select optional skills (Use <space> to select, <enter> to continue)',
message:
'Select optional skills (Use <space> to select, <enter> to continue)',
options: orderedExtraSkills.map((extraSkill) => ({
value: extraSkill.value,
label: extraSkill.label,
Expand Down Expand Up @@ -384,7 +392,11 @@ type ExtraSkill = {
order?: 'pre' | 'post';
};

async function runCommand(command: string, cwd: string, packageManager: string) {
async function runCommand(
command: string,
cwd: string,
packageManager: string,
) {
// Replace `npm create` with the equivalent command for the detected package manager
if (command.startsWith('npm create ')) {
const createReplacements: Record<string, string> = {
Expand Down Expand Up @@ -415,17 +427,17 @@ async function runCommand(command: string, cwd: string, packageManager: string)
});

if (result.exitCode !== 0) {
const details = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
const details = [result.stderr, result.stdout]
.filter(Boolean)
.join('\n')
.trim();
throw new Error(
`Failed to run command: ${command}${details ? `\n${details}` : ''}`,
);
}
}

async function runSkillCommand(
skills: ExtraSkill[],
cwd: string,
) {
async function runSkillCommand(skills: ExtraSkill[], cwd: string) {
const [firstSkill] = skills;
// `skills add` accepts repeated `--skill` flags for a single source.
const installArgs = skills.flatMap((skill) => [
Expand Down Expand Up @@ -465,7 +477,9 @@ async function runSkillCommand(
const result = await proc;

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

const templateName = await getTemplateName(argv);
const tools = await getTools(argv, extraTools, templateName);
const skills = await getSkills(argv, extraSkills, templateName, tools, multiselect);
const skills = await getSkills(
argv,
extraSkills,
templateName,
tools,
multiselect,
);

const srcFolder = path.join(root, `template-${templateName}`);
const commonFolder = path.join(root, 'template-common');
Expand Down
5 changes: 5 additions & 0 deletions template-rslint/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Tools

### Rslint

- Run `{{ packageManager }} run lint` to lint your code
11 changes: 11 additions & 0 deletions template-rslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "rslint",
Comment thread
chenjiahan marked this conversation as resolved.
"private": true,
"version": "1.0.0",
"scripts": {
"lint": "rslint"
},
"devDependencies": {
"@rslint/core": "^0.4.2"
Comment thread
chenjiahan marked this conversation as resolved.
}
}
3 changes: 3 additions & 0 deletions template-rslint/rslint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig, ts } from '@rslint/core';

export default defineConfig([ts.configs.recommended]);
32 changes: 32 additions & 0 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,38 @@ test('should accept comma separated tools option', async () => {
expect(fs.existsSync(path.join(projectDir, '.prettierrc'))).toBe(true);
});

test('should scaffold rslint tool files', async () => {
const projectDir = path.join(testDir, 'rslint-tool');

await create({
name: 'test',
root: fixturesDir,
templates: ['vanilla'],
getTemplateName: async () => 'vanilla',
argv: [
'node',
'test',
'--dir',
projectDir,
'--template',
'vanilla',
'--tools',
'rslint',
],
});

expect(fs.existsSync(path.join(projectDir, 'rslint.config.ts'))).toBe(true);

const packageJson = JSON.parse(
fs.readFileSync(path.join(projectDir, 'package.json'), 'utf-8'),
);

expect(packageJson.scripts).toMatchObject({
lint: 'rslint',
});
expect(packageJson.devDependencies['@rslint/core']).toBeTruthy();
});

test('should skip tools selection', async () => {
const projectDir = path.join(testDir, 'comma-separated-tools');

Expand Down
12 changes: 10 additions & 2 deletions test/help.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test('help message includes extra tools', async () => {
}

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

test('help message hides skill help when no optional skills are configured', async () => {
Expand Down Expand Up @@ -133,7 +133,15 @@ test('help message lists all optional skills even when template and tools are pr
when: ({ tools }) => tools.includes('rstest'),
},
],
argv: ['node', 'test', '--help', '--template', 'vanilla', '--tools', 'biome'],
argv: [
'node',
'test',
'--help',
'--template',
'vanilla',
'--tools',
'biome',
],
});
} finally {
logger.override({
Expand Down
Loading