Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion packages/create-rslib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"test": "rstest"
},
"dependencies": {
"create-rstack": "1.7.4"
"create-rstack": "1.7.6"
},
"devDependencies": {
"@rslib/tsconfig": "workspace:*",
Expand Down
24 changes: 15 additions & 9 deletions packages/create-rslib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
type Argv,
BUILTIN_TOOLS,
checkCancel,
create,
type ESLintTemplateName,
Expand All @@ -16,15 +17,20 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));

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

async function getTemplateName({ template }: Argv) {
if (typeof template === 'string') {
const pair = template.split('-');
const lang = pair[pair.length - 1];
if (lang && ['js', 'ts'].includes(lang)) {
return template;
}
// default to ts
return `${template}-ts`;
async function getTemplateName(argv: Argv) {
if (typeof argv.template === 'string') {
const pair = argv.template.split('-');
const lang = pair[pair.length - 1] ?? 'ts';
const rest = pair.slice(0, pair.length - 1).join('-');
const tools = (
typeof argv.tools === 'string' ? [argv.tools] : (argv.tools ?? [])
).filter((tool) => !BUILTIN_TOOLS.includes(tool));

return composeTemplateName({
template: rest,
lang: lang as Lang,
tools,
});
}

const templateName = checkCancel<TemplateName>(
Expand Down
64 changes: 32 additions & 32 deletions packages/create-rslib/test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@ import { existsSync } from 'node:fs';
import path from 'node:path';
import { expect } from '@rstest/core';
import fse from 'fs-extra';

export const decomposeTemplateName = (
name: string,
): {
template: string;
tools: string[];
lang: string;
} => {
const [template, tools, lang] = name.split('-');
const trimBrackets = (str: string) => str.replace(/^\[|\]$/g, '');
return {
template: trimBrackets(template)!,
tools: tools === '[]' ? [] : trimBrackets(tools).split(','),
lang: lang!,
} as const;
};
import type { Lang } from '../src/helpers';

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

export interface TemplateCase {
template: string;
lang: Lang;
tools: string[];
label: string;
}

export const createAndValidate = (
cwd: string,
template: string,
templateCase: TemplateCase,
{
name = `test-temp-${template}`,
tools = [],
name = `test-temp-${templateCase.label}`,
tools: additionalTools = [],
clean = true,
}: {
name?: string;
Expand All @@ -46,9 +38,19 @@ export const createAndValidate = (
const dir = path.join(cwd, name);
fse.removeSync(dir);

let command = `node ../dist/index.js -d ${name} -t ${template}`;
if (tools.length) {
const toolsCmd = tools.map((tool) => `--tools ${tool}`).join(' ');
const templateArg = `${templateCase.template}-${templateCase.lang}`;

let command = `node ../dist/index.js --dir ${name} --template ${templateArg}`;

if (templateCase.tools.length) {
const templateToolsCmd = templateCase.tools
.map((tool) => `--tools ${tool}`)
.join(' ');
command += ` ${templateToolsCmd}`;
}

if (additionalTools.length) {
const toolsCmd = additionalTools.map((tool) => `--tools ${tool}`).join(' ');
command += ` ${toolsCmd}`;
}

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

const templateData = decomposeTemplateName(template);

// tsconfig
if (templateData.lang === 'ts') {
if (templateCase.lang === 'ts') {
expect(pkgJson.devDependencies.typescript).toBeTruthy();
expect(existsSync(path.join(dir, 'tsconfig.json'))).toBeTruthy();
}

// tool - Vitest
if (templateData.tools.includes('vitest')) {
if (templateCase.tools.includes('vitest')) {
for (const file of [
`vitest.config.${templateData.lang}`,
`tests/index.test.${templateData.lang}${templateData.template === 'react' ? 'x' : ''}`,
`vitest.config.${templateCase.lang}`,
`tests/index.test.${templateCase.lang}${templateCase.template === 'react' ? 'x' : ''}`,
]) {
expect(existsSync(path.join(dir, file))).toBeTruthy();
}
expect(pkgJson.devDependencies.vitest).toBeTruthy();
if (templateData.template === 'react') {
if (templateCase.template === 'react') {
expect(pkgJson.devDependencies['@testing-library/react']).toBeTruthy();
}
}

// tool - Storybook
if (templateData.tools.includes('storybook')) {
if (templateCase.tools.includes('storybook')) {
for (const file of [
`.storybook/main.${templateData.lang}`,
`.storybook/preview.${templateData.lang}`,
`.storybook/main.${templateCase.lang}`,
`.storybook/preview.${templateCase.lang}`,
]) {
expect(existsSync(path.join(dir, file))).toBeTruthy();
}
Expand Down
149 changes: 81 additions & 68 deletions packages/create-rslib/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,109 +1,122 @@
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, test } from '@rstest/core';
import { composeTemplateName, TEMPLATES } from '../src/helpers';
import { createAndValidate } from './helper';

const CASES_NODE_DUAL = [
'[node-dual]-[]-js',
'[node-dual]-[]-ts',
'[node-dual]-[rstest]-js',
'[node-dual]-[rstest]-ts',
'[node-dual]-[vitest]-js',
'[node-dual]-[vitest]-ts',
import { composeTemplateName, type Lang, TEMPLATES } from '../src/helpers';
import { createAndValidate, type TemplateCase } from './helper';

const createCase = (
template: string,
lang: Lang,
tools: string[] = [],
): TemplateCase => ({
template,
lang,
tools,
label: composeTemplateName({ template, lang, tools }),
});

const CASES_NODE_DUAL: TemplateCase[] = [
createCase('node-dual', 'js'),
createCase('node-dual', 'ts'),
createCase('node-dual', 'js', ['rstest']),
createCase('node-dual', 'ts', ['rstest']),
createCase('node-dual', 'js', ['vitest']),
createCase('node-dual', 'ts', ['vitest']),
];

const CASES_NODE_ESM = [
'[node-esm]-[]-js',
'[node-esm]-[]-ts',
'[node-esm]-[rstest]-js',
'[node-esm]-[rstest]-ts',
'[node-esm]-[vitest]-js',
'[node-esm]-[vitest]-ts',
const CASES_NODE_ESM: TemplateCase[] = [
createCase('node-esm', 'js'),
createCase('node-esm', 'ts'),
createCase('node-esm', 'js', ['rstest']),
createCase('node-esm', 'ts', ['rstest']),
createCase('node-esm', 'js', ['vitest']),
createCase('node-esm', 'ts', ['vitest']),
];

const CASES_REACT = [
'[react]-[]-js',
'[react]-[]-ts',
'[react]-[rstest,storybook]-js',
'[react]-[rstest,storybook]-ts',
'[react]-[storybook,vitest]-js',
'[react]-[storybook,vitest]-ts',
'[react]-[storybook]-js',
'[react]-[rstest]-js',
'[react]-[vitest]-js',
'[react]-[storybook]-ts',
'[react]-[rstest]-ts',
'[react]-[vitest]-ts',
const CASES_REACT: TemplateCase[] = [
createCase('react', 'js'),
createCase('react', 'ts'),
createCase('react', 'js', ['rstest', 'storybook']),
createCase('react', 'ts', ['rstest', 'storybook']),
createCase('react', 'js', ['storybook', 'vitest']),
createCase('react', 'ts', ['storybook', 'vitest']),
createCase('react', 'js', ['storybook']),
createCase('react', 'js', ['rstest']),
createCase('react', 'js', ['vitest']),
createCase('react', 'ts', ['storybook']),
createCase('react', 'ts', ['rstest']),
createCase('react', 'ts', ['vitest']),
];

const CASES_VUE = [
'[vue]-[]-js',
'[vue]-[]-ts',
'[vue]-[rstest,storybook]-js',
'[vue]-[rstest,storybook]-ts',
'[vue]-[storybook,vitest]-js',
'[vue]-[storybook,vitest]-ts',
'[vue]-[storybook]-js',
'[vue]-[storybook]-ts',
'[vue]-[rstest]-js',
'[vue]-[rstest]-ts',
'[vue]-[vitest]-js',
'[vue]-[vitest]-ts',
const CASES_VUE: TemplateCase[] = [
createCase('vue', 'js'),
createCase('vue', 'ts'),
createCase('vue', 'js', ['rstest', 'storybook']),
createCase('vue', 'ts', ['rstest', 'storybook']),
createCase('vue', 'js', ['storybook', 'vitest']),
createCase('vue', 'ts', ['storybook', 'vitest']),
createCase('vue', 'js', ['storybook']),
createCase('vue', 'ts', ['storybook']),
createCase('vue', 'js', ['rstest']),
createCase('vue', 'ts', ['rstest']),
createCase('vue', 'js', ['vitest']),
createCase('vue', 'ts', ['vitest']),
];

const ALL_CASES = [
...CASES_NODE_DUAL,
...CASES_NODE_ESM,
...CASES_REACT,
...CASES_VUE,
];

const BASE_NODE_ESM_JS = createCase('node-esm', 'js');

test('exhaust all cases', () => {
expect(
TEMPLATES.map((t) =>
composeTemplateName({
template: t.template,
lang: t.lang,
tools: Object.keys(t.tools || {}),
}),
).sort(),
).toEqual(
[
...CASES_NODE_DUAL,
...CASES_NODE_ESM,
...CASES_REACT,
...CASES_VUE,
].sort(),
);
const expected = ALL_CASES.map((item) => item.label).sort();
const actual = TEMPLATES.map((t) =>
composeTemplateName({
template: t.template,
lang: t.lang,
tools: Object.keys(t.tools || {}),
}),
).sort();
expect(actual).toEqual(expected);
});

describe('node-dual', () => {
for (const c of CASES_NODE_DUAL) {
test(`should create ${c} project as expected`, async () => {
test(`should create ${c.label} project as expected`, async () => {
createAndValidate(__dirname, c);
});
}
});

describe('node-esm', () => {
for (const c of CASES_NODE_ESM) {
test(`should create ${c} project as expected`, async () => {
test(`should create ${c.label} project as expected`, async () => {
createAndValidate(__dirname, c);
});
}
});

describe('react', () => {
for (const c of CASES_REACT) {
test(`should create ${c} project as expected`, async () => {
test(`should create ${c.label} project as expected`, async () => {
createAndValidate(__dirname, c);
});
}
});

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

test('should allow to create project in relative dir', async () => {
createAndValidate(__dirname, '[node-esm]-[]-js', {
createAndValidate(__dirname, BASE_NODE_ESM_JS, {
name: './test-temp-relative-dir',
});
});
Expand All @@ -113,7 +126,7 @@ describe('linter and formatter', () => {
test('should create project with eslint as expected', async () => {
const { dir, pkgJson, clean } = createAndValidate(
__dirname,
'[node-esm]-[]-js',
BASE_NODE_ESM_JS,
{
name: 'test-temp-eslint',
tools: ['eslint'],
Expand All @@ -128,7 +141,7 @@ describe('linter and formatter', () => {
test('should create project with prettier as expected', async () => {
const { dir, pkgJson, clean } = createAndValidate(
__dirname,
'[node-esm]-[]-js',
BASE_NODE_ESM_JS,
{
name: 'test-temp-prettier',
tools: ['prettier'],
Expand All @@ -143,7 +156,7 @@ describe('linter and formatter', () => {
test('should create project with eslint and prettier as expected', async () => {
const { dir, pkgJson, clean } = createAndValidate(
__dirname,
'[node-esm]-[]-js',
BASE_NODE_ESM_JS,
{
name: 'test-temp-eslint-prettier',
tools: ['eslint', 'prettier'],
Expand All @@ -160,7 +173,7 @@ describe('linter and formatter', () => {
test('should create project with biome as expected', async () => {
const { dir, pkgJson, clean } = createAndValidate(
__dirname,
'[node-esm]-[]-js',
BASE_NODE_ESM_JS,
{
name: 'test-temp-eslint',
tools: ['biome'],
Expand Down
Loading
Loading