Skip to content

Commit 95ae7d7

Browse files
authored
fix(create-rslib): support repeatable —tools flag (#1328)
1 parent 6aa42e0 commit 95ae7d7

File tree

7 files changed

+140
-121
lines changed

7 files changed

+140
-121
lines changed

packages/create-rslib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"test": "rstest"
3030
},
3131
"dependencies": {
32-
"create-rstack": "1.7.4"
32+
"create-rstack": "1.7.6"
3333
},
3434
"devDependencies": {
3535
"@rslib/tsconfig": "workspace:*",

packages/create-rslib/src/index.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from 'node:path';
44
import { fileURLToPath } from 'node:url';
55
import {
66
type Argv,
7+
BUILTIN_TOOLS,
78
checkCancel,
89
create,
910
type ESLintTemplateName,
@@ -16,15 +17,20 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
1617

1718
type TemplateName = 'react' | 'node' | 'vue';
1819

19-
async function getTemplateName({ template }: Argv) {
20-
if (typeof template === 'string') {
21-
const pair = template.split('-');
22-
const lang = pair[pair.length - 1];
23-
if (lang && ['js', 'ts'].includes(lang)) {
24-
return template;
25-
}
26-
// default to ts
27-
return `${template}-ts`;
20+
async function getTemplateName(argv: Argv) {
21+
if (typeof argv.template === 'string') {
22+
const pair = argv.template.split('-');
23+
const lang = pair[pair.length - 1] ?? 'ts';
24+
const rest = pair.slice(0, pair.length - 1).join('-');
25+
const tools = (
26+
typeof argv.tools === 'string' ? [argv.tools] : (argv.tools ?? [])
27+
).filter((tool) => !BUILTIN_TOOLS.includes(tool));
28+
29+
return composeTemplateName({
30+
template: rest,
31+
lang: lang as Lang,
32+
tools,
33+
});
2834
}
2935

3036
const templateName = checkCancel<TemplateName>(

packages/create-rslib/test/helper.ts

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,7 @@ import { existsSync } from 'node:fs';
33
import path from 'node:path';
44
import { expect } from '@rstest/core';
55
import fse from 'fs-extra';
6-
7-
export const decomposeTemplateName = (
8-
name: string,
9-
): {
10-
template: string;
11-
tools: string[];
12-
lang: string;
13-
} => {
14-
const [template, tools, lang] = name.split('-');
15-
const trimBrackets = (str: string) => str.replace(/^\[|\]$/g, '');
16-
return {
17-
template: trimBrackets(template)!,
18-
tools: tools === '[]' ? [] : trimBrackets(tools).split(','),
19-
lang: lang!,
20-
} as const;
21-
};
6+
import type { Lang } from '../src/helpers';
227

238
export const expectPackageJson = (
249
pkgJson: Record<string, any>,
@@ -30,12 +15,19 @@ export const expectPackageJson = (
3015
expect(pkgJson.devDependencies['@rslib/core']).toBeTruthy();
3116
};
3217

18+
export interface TemplateCase {
19+
template: string;
20+
lang: Lang;
21+
tools: string[];
22+
label: string;
23+
}
24+
3325
export const createAndValidate = (
3426
cwd: string,
35-
template: string,
27+
templateCase: TemplateCase,
3628
{
37-
name = `test-temp-${template}`,
38-
tools = [],
29+
name = `test-temp-${templateCase.label}`,
30+
tools: additionalTools = [],
3931
clean = true,
4032
}: {
4133
name?: string;
@@ -46,9 +38,19 @@ export const createAndValidate = (
4638
const dir = path.join(cwd, name);
4739
fse.removeSync(dir);
4840

49-
let command = `node ../dist/index.js -d ${name} -t ${template}`;
50-
if (tools.length) {
51-
const toolsCmd = tools.map((tool) => `--tools ${tool}`).join(' ');
41+
const templateArg = `${templateCase.template}-${templateCase.lang}`;
42+
43+
let command = `node ../dist/index.js --dir ${name} --template ${templateArg}`;
44+
45+
if (templateCase.tools.length) {
46+
const templateToolsCmd = templateCase.tools
47+
.map((tool) => `--tools ${tool}`)
48+
.join(' ');
49+
command += ` ${templateToolsCmd}`;
50+
}
51+
52+
if (additionalTools.length) {
53+
const toolsCmd = additionalTools.map((tool) => `--tools ${tool}`).join(' ');
5254
command += ` ${toolsCmd}`;
5355
}
5456

@@ -57,33 +59,31 @@ export const createAndValidate = (
5759
const pkgJson = fse.readJSONSync(path.join(dir, 'package.json'));
5860
expectPackageJson(pkgJson, path.basename(name));
5961

60-
const templateData = decomposeTemplateName(template);
61-
6262
// tsconfig
63-
if (templateData.lang === 'ts') {
63+
if (templateCase.lang === 'ts') {
6464
expect(pkgJson.devDependencies.typescript).toBeTruthy();
6565
expect(existsSync(path.join(dir, 'tsconfig.json'))).toBeTruthy();
6666
}
6767

6868
// tool - Vitest
69-
if (templateData.tools.includes('vitest')) {
69+
if (templateCase.tools.includes('vitest')) {
7070
for (const file of [
71-
`vitest.config.${templateData.lang}`,
72-
`tests/index.test.${templateData.lang}${templateData.template === 'react' ? 'x' : ''}`,
71+
`vitest.config.${templateCase.lang}`,
72+
`tests/index.test.${templateCase.lang}${templateCase.template === 'react' ? 'x' : ''}`,
7373
]) {
7474
expect(existsSync(path.join(dir, file))).toBeTruthy();
7575
}
7676
expect(pkgJson.devDependencies.vitest).toBeTruthy();
77-
if (templateData.template === 'react') {
77+
if (templateCase.template === 'react') {
7878
expect(pkgJson.devDependencies['@testing-library/react']).toBeTruthy();
7979
}
8080
}
8181

8282
// tool - Storybook
83-
if (templateData.tools.includes('storybook')) {
83+
if (templateCase.tools.includes('storybook')) {
8484
for (const file of [
85-
`.storybook/main.${templateData.lang}`,
86-
`.storybook/preview.${templateData.lang}`,
85+
`.storybook/main.${templateCase.lang}`,
86+
`.storybook/preview.${templateCase.lang}`,
8787
]) {
8888
expect(existsSync(path.join(dir, file))).toBeTruthy();
8989
}

packages/create-rslib/test/index.test.ts

Lines changed: 81 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,122 @@
11
import { existsSync } from 'node:fs';
22
import { join } from 'node:path';
33
import { describe, expect, test } from '@rstest/core';
4-
import { composeTemplateName, TEMPLATES } from '../src/helpers';
5-
import { createAndValidate } from './helper';
6-
7-
const CASES_NODE_DUAL = [
8-
'[node-dual]-[]-js',
9-
'[node-dual]-[]-ts',
10-
'[node-dual]-[rstest]-js',
11-
'[node-dual]-[rstest]-ts',
12-
'[node-dual]-[vitest]-js',
13-
'[node-dual]-[vitest]-ts',
4+
import { composeTemplateName, type Lang, TEMPLATES } from '../src/helpers';
5+
import { createAndValidate, type TemplateCase } from './helper';
6+
7+
const createCase = (
8+
template: string,
9+
lang: Lang,
10+
tools: string[] = [],
11+
): TemplateCase => ({
12+
template,
13+
lang,
14+
tools,
15+
label: composeTemplateName({ template, lang, tools }),
16+
});
17+
18+
const CASES_NODE_DUAL: TemplateCase[] = [
19+
createCase('node-dual', 'js'),
20+
createCase('node-dual', 'ts'),
21+
createCase('node-dual', 'js', ['rstest']),
22+
createCase('node-dual', 'ts', ['rstest']),
23+
createCase('node-dual', 'js', ['vitest']),
24+
createCase('node-dual', 'ts', ['vitest']),
1425
];
1526

16-
const CASES_NODE_ESM = [
17-
'[node-esm]-[]-js',
18-
'[node-esm]-[]-ts',
19-
'[node-esm]-[rstest]-js',
20-
'[node-esm]-[rstest]-ts',
21-
'[node-esm]-[vitest]-js',
22-
'[node-esm]-[vitest]-ts',
27+
const CASES_NODE_ESM: TemplateCase[] = [
28+
createCase('node-esm', 'js'),
29+
createCase('node-esm', 'ts'),
30+
createCase('node-esm', 'js', ['rstest']),
31+
createCase('node-esm', 'ts', ['rstest']),
32+
createCase('node-esm', 'js', ['vitest']),
33+
createCase('node-esm', 'ts', ['vitest']),
2334
];
2435

25-
const CASES_REACT = [
26-
'[react]-[]-js',
27-
'[react]-[]-ts',
28-
'[react]-[rstest,storybook]-js',
29-
'[react]-[rstest,storybook]-ts',
30-
'[react]-[storybook,vitest]-js',
31-
'[react]-[storybook,vitest]-ts',
32-
'[react]-[storybook]-js',
33-
'[react]-[rstest]-js',
34-
'[react]-[vitest]-js',
35-
'[react]-[storybook]-ts',
36-
'[react]-[rstest]-ts',
37-
'[react]-[vitest]-ts',
36+
const CASES_REACT: TemplateCase[] = [
37+
createCase('react', 'js'),
38+
createCase('react', 'ts'),
39+
createCase('react', 'js', ['rstest', 'storybook']),
40+
createCase('react', 'ts', ['rstest', 'storybook']),
41+
createCase('react', 'js', ['storybook', 'vitest']),
42+
createCase('react', 'ts', ['storybook', 'vitest']),
43+
createCase('react', 'js', ['storybook']),
44+
createCase('react', 'js', ['rstest']),
45+
createCase('react', 'js', ['vitest']),
46+
createCase('react', 'ts', ['storybook']),
47+
createCase('react', 'ts', ['rstest']),
48+
createCase('react', 'ts', ['vitest']),
3849
];
3950

40-
const CASES_VUE = [
41-
'[vue]-[]-js',
42-
'[vue]-[]-ts',
43-
'[vue]-[rstest,storybook]-js',
44-
'[vue]-[rstest,storybook]-ts',
45-
'[vue]-[storybook,vitest]-js',
46-
'[vue]-[storybook,vitest]-ts',
47-
'[vue]-[storybook]-js',
48-
'[vue]-[storybook]-ts',
49-
'[vue]-[rstest]-js',
50-
'[vue]-[rstest]-ts',
51-
'[vue]-[vitest]-js',
52-
'[vue]-[vitest]-ts',
51+
const CASES_VUE: TemplateCase[] = [
52+
createCase('vue', 'js'),
53+
createCase('vue', 'ts'),
54+
createCase('vue', 'js', ['rstest', 'storybook']),
55+
createCase('vue', 'ts', ['rstest', 'storybook']),
56+
createCase('vue', 'js', ['storybook', 'vitest']),
57+
createCase('vue', 'ts', ['storybook', 'vitest']),
58+
createCase('vue', 'js', ['storybook']),
59+
createCase('vue', 'ts', ['storybook']),
60+
createCase('vue', 'js', ['rstest']),
61+
createCase('vue', 'ts', ['rstest']),
62+
createCase('vue', 'js', ['vitest']),
63+
createCase('vue', 'ts', ['vitest']),
5364
];
5465

66+
const ALL_CASES = [
67+
...CASES_NODE_DUAL,
68+
...CASES_NODE_ESM,
69+
...CASES_REACT,
70+
...CASES_VUE,
71+
];
72+
73+
const BASE_NODE_ESM_JS = createCase('node-esm', 'js');
74+
5575
test('exhaust all cases', () => {
56-
expect(
57-
TEMPLATES.map((t) =>
58-
composeTemplateName({
59-
template: t.template,
60-
lang: t.lang,
61-
tools: Object.keys(t.tools || {}),
62-
}),
63-
).sort(),
64-
).toEqual(
65-
[
66-
...CASES_NODE_DUAL,
67-
...CASES_NODE_ESM,
68-
...CASES_REACT,
69-
...CASES_VUE,
70-
].sort(),
71-
);
76+
const expected = ALL_CASES.map((item) => item.label).sort();
77+
const actual = TEMPLATES.map((t) =>
78+
composeTemplateName({
79+
template: t.template,
80+
lang: t.lang,
81+
tools: Object.keys(t.tools || {}),
82+
}),
83+
).sort();
84+
expect(actual).toEqual(expected);
7285
});
7386

7487
describe('node-dual', () => {
7588
for (const c of CASES_NODE_DUAL) {
76-
test(`should create ${c} project as expected`, async () => {
89+
test(`should create ${c.label} project as expected`, async () => {
7790
createAndValidate(__dirname, c);
7891
});
7992
}
8093
});
8194

8295
describe('node-esm', () => {
8396
for (const c of CASES_NODE_ESM) {
84-
test(`should create ${c} project as expected`, async () => {
97+
test(`should create ${c.label} project as expected`, async () => {
8598
createAndValidate(__dirname, c);
8699
});
87100
}
88101
});
89102

90103
describe('react', () => {
91104
for (const c of CASES_REACT) {
92-
test(`should create ${c} project as expected`, async () => {
105+
test(`should create ${c.label} project as expected`, async () => {
93106
createAndValidate(__dirname, c);
94107
});
95108
}
96109
});
97110

98111
describe('custom path to create', () => {
99112
test('should allow to create project in sub dir', async () => {
100-
createAndValidate(__dirname, '[node-esm]-[]-js', {
113+
createAndValidate(__dirname, BASE_NODE_ESM_JS, {
101114
name: 'test-temp-dir/rslib-project',
102115
});
103116
});
104117

105118
test('should allow to create project in relative dir', async () => {
106-
createAndValidate(__dirname, '[node-esm]-[]-js', {
119+
createAndValidate(__dirname, BASE_NODE_ESM_JS, {
107120
name: './test-temp-relative-dir',
108121
});
109122
});
@@ -113,7 +126,7 @@ describe('linter and formatter', () => {
113126
test('should create project with eslint as expected', async () => {
114127
const { dir, pkgJson, clean } = createAndValidate(
115128
__dirname,
116-
'[node-esm]-[]-js',
129+
BASE_NODE_ESM_JS,
117130
{
118131
name: 'test-temp-eslint',
119132
tools: ['eslint'],
@@ -128,7 +141,7 @@ describe('linter and formatter', () => {
128141
test('should create project with prettier as expected', async () => {
129142
const { dir, pkgJson, clean } = createAndValidate(
130143
__dirname,
131-
'[node-esm]-[]-js',
144+
BASE_NODE_ESM_JS,
132145
{
133146
name: 'test-temp-prettier',
134147
tools: ['prettier'],
@@ -143,7 +156,7 @@ describe('linter and formatter', () => {
143156
test('should create project with eslint and prettier as expected', async () => {
144157
const { dir, pkgJson, clean } = createAndValidate(
145158
__dirname,
146-
'[node-esm]-[]-js',
159+
BASE_NODE_ESM_JS,
147160
{
148161
name: 'test-temp-eslint-prettier',
149162
tools: ['eslint', 'prettier'],
@@ -160,7 +173,7 @@ describe('linter and formatter', () => {
160173
test('should create project with biome as expected', async () => {
161174
const { dir, pkgJson, clean } = createAndValidate(
162175
__dirname,
163-
'[node-esm]-[]-js',
176+
BASE_NODE_ESM_JS,
164177
{
165178
name: 'test-temp-eslint',
166179
tools: ['biome'],

0 commit comments

Comments
 (0)