create-packkit 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/cli/args.js +3 -0
- package/src/cli/index.js +4 -4
- package/src/core/features/agents.js +1 -1
- package/src/core/features/bundler.js +1 -1
- package/src/core/features/frameworks.js +156 -0
- package/src/core/features/index.js +6 -2
- package/src/core/features/meta.js +26 -35
- package/src/core/features/release.js +1 -1
- package/src/core/features/storybook.js +87 -0
- package/src/core/features/test.js +39 -11
- package/src/core/features/typescript.js +4 -3
- package/src/core/features/vite.js +66 -0
- package/src/core/features/workflows.js +2 -2
- package/src/core/options.js +31 -4
- package/src/core/presets.js +6 -0
- package/src/core/features/react.js +0 -25
package/README.md
CHANGED
|
@@ -34,8 +34,9 @@ No install needed: **[danmat.github.io/create-packkit](https://danmat.github.io/
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| **Language** | TypeScript (strict) · JavaScript (ESM) |
|
|
36
36
|
| **Module format** | ESM · CJS · dual (proper `exports` map) |
|
|
37
|
-
| **Target** | Library · CLI tool · **HTTP service (Hono)** · any combination |
|
|
38
|
-
| **Framework** | None · **React**
|
|
37
|
+
| **Target** | Library · CLI tool · **HTTP service (Hono)** · **App (Vite SPA)** · any combination |
|
|
38
|
+
| **Framework** | None · **React** · **Vue** · **Svelte** (component libraries or apps) |
|
|
39
|
+
| **Storybook** | optional, for React / Vue / Svelte component libraries |
|
|
39
40
|
| **Bundler** | tsup · tsdown · unbuild · rollup · none (tsc) · optional **minify** |
|
|
40
41
|
| **Tests** | Vitest · Jest · node:test · none (+ coverage) |
|
|
41
42
|
| **Lint/format** | ESLint + Prettier · Biome · oxlint · none |
|
|
@@ -48,7 +49,7 @@ No install needed: **[danmat.github.io/create-packkit](https://danmat.github.io/
|
|
|
48
49
|
|
|
49
50
|
## Presets
|
|
50
51
|
|
|
51
|
-
`ts-lib` · `js-lib` · `ts-cli` / `cli` · `react-lib` · `react-lib-js` · `node-service` · `oss` · `minimal` · `full` — named bundles of the options above. See the [roadmap](ROADMAP.md) for what's next.
|
|
52
|
+
`ts-lib` · `js-lib` · `ts-cli` / `cli` · `react-lib` · `react-lib-js` · `react-app` · `vue-lib` · `svelte-lib` · `node-service` · `oss` · `minimal` · `full` — named bundles of the options above. See the [roadmap](ROADMAP.md) for what's next.
|
|
52
53
|
|
|
53
54
|
## How it works
|
|
54
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-packkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Highly configurable scaffolder for modern npm packages and CLIs — pick your stack (TS/JS, bundler, tests, linter, CI, releases) from a CLI or a web configurator.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/cli/args.js
CHANGED
|
@@ -42,6 +42,8 @@ export function parseCliArgs(argv) {
|
|
|
42
42
|
minify: { type: 'boolean' },
|
|
43
43
|
target: { type: 'string', multiple: true },
|
|
44
44
|
workflows: { type: 'string', multiple: true },
|
|
45
|
+
minify: { type: 'boolean' },
|
|
46
|
+
storybook: { type: 'boolean' },
|
|
45
47
|
help: { type: 'boolean', short: 'h' },
|
|
46
48
|
version: { type: 'boolean', short: 'v' },
|
|
47
49
|
...Object.fromEntries(Object.keys(OVERRIDE_FLAGS).map((k) => [k, { type: 'string' }])),
|
|
@@ -62,6 +64,7 @@ export function parseCliArgs(argv) {
|
|
|
62
64
|
if (values.target) overrides.target = values.target;
|
|
63
65
|
if (values.workflows) overrides.workflows = values.workflows;
|
|
64
66
|
if (values.minify) overrides.minify = true;
|
|
67
|
+
if (values.storybook) overrides.storybook = true;
|
|
65
68
|
for (const [flag, key] of Object.entries(NEGATABLE)) {
|
|
66
69
|
if (values[flag]) overrides[key] = false;
|
|
67
70
|
}
|
package/src/cli/index.js
CHANGED
|
@@ -33,8 +33,8 @@ Options:
|
|
|
33
33
|
--no-install Skip dependency install
|
|
34
34
|
--no-git Skip git init
|
|
35
35
|
--pm <manager> npm | pnpm | yarn | bun
|
|
36
|
-
--language <ts|js> --module <esm|cjs|dual> --framework <none|react>
|
|
37
|
-
--target <library|cli|service> (repeatable)
|
|
36
|
+
--language <ts|js> --module <esm|cjs|dual> --framework <none|react|vue|svelte>
|
|
37
|
+
--target <library|cli|service|app> (repeatable) --storybook
|
|
38
38
|
--bundler <tsup|tsdown|unbuild|rollup|none> --minify
|
|
39
39
|
--test <vitest|jest|node|none> --lint <eslint-prettier|biome|oxlint|none>
|
|
40
40
|
--hooks <simple-git-hooks|husky|lefthook|none> --release <changesets|release-it|np|none>
|
|
@@ -98,8 +98,8 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
98
98
|
const next = [
|
|
99
99
|
args.here ? null : `cd ${rel}`,
|
|
100
100
|
config.install ? null : `${config.packageManager} install`,
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
config.hasApp ? `${runWord(config)} dev` : config.hasBuild ? `${runWord(config)} build` : null,
|
|
102
|
+
config.test !== 'none' ? `${runWord(config)} test` : null,
|
|
103
103
|
].filter(Boolean);
|
|
104
104
|
|
|
105
105
|
const done = `Created ${summary.name} — ${summary.fileCount} files · ${summary.stack.join(' · ')}`;
|
|
@@ -12,7 +12,7 @@ export default {
|
|
|
12
12
|
if (cfg.isTs) commands.push(`- Type-check: \`${run('typecheck')}\``);
|
|
13
13
|
if (cfg.lint !== 'none') commands.push(`- Lint: \`${run('lint')}\``);
|
|
14
14
|
if (cfg.test !== 'none') commands.push(`- Test: \`${test}\``);
|
|
15
|
-
if (cfg.
|
|
15
|
+
if (cfg.hasBuild) commands.push(`- Build: \`${run('build')}\``);
|
|
16
16
|
|
|
17
17
|
const stack = [
|
|
18
18
|
`- Language: ${cfg.isTs ? 'TypeScript (strict)' : 'JavaScript (ESM)'}`,
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// React / Vue / Svelte source + dependencies, for both component libraries and
|
|
2
|
+
// Vite apps. The Vite build wiring lives in vite.js; here we emit the source
|
|
3
|
+
// files, the runtime deps (peer for libs, direct for apps), and — for Svelte
|
|
4
|
+
// libraries, which ship uncompiled source — the package entry points.
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
id: 'frameworks',
|
|
8
|
+
active: (cfg) => cfg.hasFramework,
|
|
9
|
+
apply(cfg) {
|
|
10
|
+
const files = {};
|
|
11
|
+
const pkg = { devDependencies: {}, scripts: {} };
|
|
12
|
+
const forApp = cfg.hasApp;
|
|
13
|
+
|
|
14
|
+
if (cfg.isReact) react(cfg, files, pkg, forApp);
|
|
15
|
+
else if (cfg.isVue) vue(cfg, files, pkg, forApp);
|
|
16
|
+
else if (cfg.isSvelte) svelte(cfg, files, pkg, forApp);
|
|
17
|
+
|
|
18
|
+
return { files, pkg };
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/* --------------------------------- React --------------------------------- */
|
|
23
|
+
function react(cfg, files, pkg, forApp) {
|
|
24
|
+
const x = cfg.isTs ? 'tsx' : 'jsx';
|
|
25
|
+
if (forApp) {
|
|
26
|
+
files['index.html'] = htmlShell(cfg, `/src/main.${x}`);
|
|
27
|
+
files[`src/main.${x}`] = [
|
|
28
|
+
`import { StrictMode } from 'react';`,
|
|
29
|
+
`import { createRoot } from 'react-dom/client';`,
|
|
30
|
+
`import { App } from './App.${x === 'tsx' ? 'js' : 'js'}';`,
|
|
31
|
+
``,
|
|
32
|
+
`createRoot(document.getElementById('root')${cfg.isTs ? '!' : ''}).render(`,
|
|
33
|
+
`\t<StrictMode><App /></StrictMode>,`,
|
|
34
|
+
`);`,
|
|
35
|
+
``,
|
|
36
|
+
].join('\n');
|
|
37
|
+
files[`src/App.${x}`] = [
|
|
38
|
+
`export function App() {`,
|
|
39
|
+
`\treturn <h1>Hello from ${cfg.name}</h1>;`,
|
|
40
|
+
`}`,
|
|
41
|
+
``,
|
|
42
|
+
].join('\n');
|
|
43
|
+
pkg.dependencies = { react: '^18.3.0', 'react-dom': '^18.3.0' };
|
|
44
|
+
} else {
|
|
45
|
+
files[`src/index.${x}`] = cfg.isTs
|
|
46
|
+
? [
|
|
47
|
+
`export interface ButtonProps {`,
|
|
48
|
+
`\tlabel: string;`,
|
|
49
|
+
`\tonClick?: () => void;`,
|
|
50
|
+
`}`,
|
|
51
|
+
``,
|
|
52
|
+
`export function Button({ label, onClick }: ButtonProps) {`,
|
|
53
|
+
`\treturn <button onClick={onClick}>{label}</button>;`,
|
|
54
|
+
`}`,
|
|
55
|
+
``,
|
|
56
|
+
].join('\n')
|
|
57
|
+
: [`export function Button({ label, onClick }) {`, `\treturn <button onClick={onClick}>{label}</button>;`, `}`, ``].join('\n');
|
|
58
|
+
pkg.peerDependencies = { react: '>=18', 'react-dom': '>=18' };
|
|
59
|
+
pkg.devDependencies.react = '^18.3.0';
|
|
60
|
+
pkg.devDependencies['react-dom'] = '^18.3.0';
|
|
61
|
+
}
|
|
62
|
+
if (cfg.isTs) {
|
|
63
|
+
pkg.devDependencies['@types/react'] = '^18.3.0';
|
|
64
|
+
pkg.devDependencies['@types/react-dom'] = '^18.3.0';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ---------------------------------- Vue ---------------------------------- */
|
|
69
|
+
function vue(cfg, files, pkg, forApp) {
|
|
70
|
+
const script = cfg.isTs ? `<script setup lang="ts">` : `<script setup>`;
|
|
71
|
+
if (forApp) {
|
|
72
|
+
files['index.html'] = htmlShell(cfg, `/src/main.${cfg.ext}`);
|
|
73
|
+
files[`src/main.${cfg.ext}`] = [
|
|
74
|
+
`import { createApp } from 'vue';`,
|
|
75
|
+
`import App from './App.vue';`,
|
|
76
|
+
``,
|
|
77
|
+
`createApp(App).mount('#root');`,
|
|
78
|
+
``,
|
|
79
|
+
].join('\n');
|
|
80
|
+
files['src/App.vue'] = [script, `</script>`, ``, `<template>`, `\t<h1>Hello from ${cfg.name}</h1>`, `</template>`, ``].join('\n');
|
|
81
|
+
pkg.dependencies = { vue: '^3.4.0' };
|
|
82
|
+
} else {
|
|
83
|
+
files[`src/index.${cfg.ext}`] = `export { default as Button } from './Button.vue';\n`;
|
|
84
|
+
files['src/Button.vue'] = [
|
|
85
|
+
script,
|
|
86
|
+
`defineProps${cfg.isTs ? '<{ label: string }>()' : "(['label'])"};`,
|
|
87
|
+
`</script>`,
|
|
88
|
+
``,
|
|
89
|
+
`<template>`,
|
|
90
|
+
`\t<button><slot>{{ label }}</slot></button>`,
|
|
91
|
+
`</template>`,
|
|
92
|
+
``,
|
|
93
|
+
].join('\n');
|
|
94
|
+
pkg.peerDependencies = { vue: '>=3' };
|
|
95
|
+
pkg.devDependencies.vue = '^3.4.0';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* -------------------------------- Svelte --------------------------------- */
|
|
100
|
+
function svelte(cfg, files, pkg, forApp) {
|
|
101
|
+
const script = cfg.isTs ? `<script lang="ts">` : `<script>`;
|
|
102
|
+
// Shared by the Vite app build and the Vitest (test) config; enables TS in SFCs.
|
|
103
|
+
files['svelte.config.js'] = `import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\nexport default { preprocess: vitePreprocess() };\n`;
|
|
104
|
+
pkg.devDependencies['@sveltejs/vite-plugin-svelte'] = '^4.0.0';
|
|
105
|
+
if (cfg.isTs) pkg.devDependencies['svelte-check'] = '^4.0.0';
|
|
106
|
+
if (forApp) {
|
|
107
|
+
files['index.html'] = htmlShell(cfg, `/src/main.${cfg.ext}`);
|
|
108
|
+
files[`src/main.${cfg.ext}`] = [
|
|
109
|
+
`import { mount } from 'svelte';`,
|
|
110
|
+
`import App from './App.svelte';`,
|
|
111
|
+
``,
|
|
112
|
+
`const app = mount(App, { target: document.getElementById('root')${cfg.isTs ? '!' : ''} });`,
|
|
113
|
+
`export default app;`,
|
|
114
|
+
``,
|
|
115
|
+
].join('\n');
|
|
116
|
+
files['src/App.svelte'] = [script, `</script>`, ``, `<h1>Hello from ${cfg.name}</h1>`, ``].join('\n');
|
|
117
|
+
pkg.dependencies = { svelte: '^5.0.0' };
|
|
118
|
+
} else {
|
|
119
|
+
// Svelte libraries ship uncompiled source; the consumer's bundler compiles.
|
|
120
|
+
files[`src/index.${cfg.ext}`] = `export { default as Button } from './Button.svelte';\n`;
|
|
121
|
+
files['src/Button.svelte'] = [
|
|
122
|
+
script,
|
|
123
|
+
cfg.isTs ? `\tinterface Props { label: string; }` : ``,
|
|
124
|
+
cfg.isTs ? `\tconst { label }: Props = $props();` : `\tconst { label } = $props();`,
|
|
125
|
+
`</script>`,
|
|
126
|
+
``,
|
|
127
|
+
`<button>{label}</button>`,
|
|
128
|
+
``,
|
|
129
|
+
].filter((l) => l !== ``).join('\n') + '\n';
|
|
130
|
+
pkg.peerDependencies = { svelte: '>=5' };
|
|
131
|
+
pkg.devDependencies.svelte = '^5.0.0';
|
|
132
|
+
// Ship source; point consumers at it.
|
|
133
|
+
pkg.svelte = `./src/index.${cfg.ext}`;
|
|
134
|
+
pkg.exports = { '.': { svelte: `./src/index.${cfg.ext}`, default: `./src/index.${cfg.ext}` } };
|
|
135
|
+
pkg.files = ['src'];
|
|
136
|
+
pkg.type = 'module';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function htmlShell(cfg, entry) {
|
|
141
|
+
return [
|
|
142
|
+
`<!doctype html>`,
|
|
143
|
+
`<html lang="en">`,
|
|
144
|
+
`\t<head>`,
|
|
145
|
+
`\t\t<meta charset="UTF-8" />`,
|
|
146
|
+
`\t\t<meta name="viewport" content="width=device-width, initial-scale=1.0" />`,
|
|
147
|
+
`\t\t<title>${cfg.name}</title>`,
|
|
148
|
+
`\t</head>`,
|
|
149
|
+
`\t<body>`,
|
|
150
|
+
`\t\t<div id="root"></div>`,
|
|
151
|
+
`\t\t<script type="module" src="${entry}"></script>`,
|
|
152
|
+
`\t</body>`,
|
|
153
|
+
`</html>`,
|
|
154
|
+
``,
|
|
155
|
+
].join('\n');
|
|
156
|
+
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import meta from './meta.js';
|
|
5
5
|
import bundler from './bundler.js';
|
|
6
6
|
import typescript from './typescript.js';
|
|
7
|
-
import
|
|
7
|
+
import frameworks from './frameworks.js';
|
|
8
|
+
import vite from './vite.js';
|
|
8
9
|
import service from './service.js';
|
|
9
10
|
import test from './test.js';
|
|
10
11
|
import lint from './lint.js';
|
|
@@ -12,6 +13,7 @@ import githooks from './githooks.js';
|
|
|
12
13
|
import release from './release.js';
|
|
13
14
|
import cli from './cli.js';
|
|
14
15
|
import workflows from './workflows.js';
|
|
16
|
+
import storybook from './storybook.js';
|
|
15
17
|
import community from './community.js';
|
|
16
18
|
import agents from './agents.js';
|
|
17
19
|
import vscode from './vscode.js';
|
|
@@ -21,7 +23,8 @@ export default [
|
|
|
21
23
|
meta,
|
|
22
24
|
bundler,
|
|
23
25
|
typescript,
|
|
24
|
-
|
|
26
|
+
frameworks,
|
|
27
|
+
vite,
|
|
25
28
|
service,
|
|
26
29
|
test,
|
|
27
30
|
lint,
|
|
@@ -29,6 +32,7 @@ export default [
|
|
|
29
32
|
release,
|
|
30
33
|
cli,
|
|
31
34
|
workflows,
|
|
35
|
+
storybook,
|
|
32
36
|
community,
|
|
33
37
|
agents,
|
|
34
38
|
vscode,
|
|
@@ -23,10 +23,11 @@ export default {
|
|
|
23
23
|
pkg.homepage = `${cfg.repo.replace(/\.git$/, '')}#readme`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// Source entry point
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
// Source entry point — only the plain library entry lives here; framework
|
|
27
|
+
// and app/cli/service modules write their own source.
|
|
28
|
+
if (cfg.hasLibrary && !cfg.hasFramework) {
|
|
29
|
+
files[`src/index.${cfg.ext}`] = libraryEntry(cfg);
|
|
30
|
+
}
|
|
30
31
|
|
|
31
32
|
// README
|
|
32
33
|
files['README.md'] = readme(cfg);
|
|
@@ -34,8 +35,15 @@ export default {
|
|
|
34
35
|
// Node version pin
|
|
35
36
|
files['.nvmrc'] = `${cfg.nodeVersion}\n`;
|
|
36
37
|
|
|
37
|
-
// A typecheck script for TS projects
|
|
38
|
-
|
|
38
|
+
// A typecheck script for TS projects — framework-aware (plain tsc can't
|
|
39
|
+
// resolve .vue/.svelte modules).
|
|
40
|
+
if (cfg.isTs) {
|
|
41
|
+
pkg.scripts.typecheck = cfg.isVue
|
|
42
|
+
? 'vue-tsc --noEmit'
|
|
43
|
+
: cfg.isSvelte
|
|
44
|
+
? 'svelte-check --tsconfig ./tsconfig.json'
|
|
45
|
+
: 'tsc --noEmit';
|
|
46
|
+
}
|
|
39
47
|
|
|
40
48
|
return { files, pkg };
|
|
41
49
|
},
|
|
@@ -65,32 +73,8 @@ function libraryEntry(cfg) {
|
|
|
65
73
|
].join('\n');
|
|
66
74
|
}
|
|
67
75
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
return [
|
|
71
|
-
`export interface ButtonProps {`,
|
|
72
|
-
`\t/** Text shown inside the button. */`,
|
|
73
|
-
`\tlabel: string;`,
|
|
74
|
-
`\tonClick?: () => void;`,
|
|
75
|
-
`}`,
|
|
76
|
-
``,
|
|
77
|
-
`/** A tiny example component — replace me. */`,
|
|
78
|
-
`export function Button({ label, onClick }: ButtonProps) {`,
|
|
79
|
-
`\treturn <button onClick={onClick}>{label}</button>;`,
|
|
80
|
-
`}`,
|
|
81
|
-
``,
|
|
82
|
-
].join('\n');
|
|
83
|
-
}
|
|
84
|
-
return [
|
|
85
|
-
`/**`,
|
|
86
|
-
` * A tiny example component — replace me.`,
|
|
87
|
-
` * @param {{ label: string, onClick?: () => void }} props`,
|
|
88
|
-
` */`,
|
|
89
|
-
`export function Button({ label, onClick }) {`,
|
|
90
|
-
`\treturn <button onClick={onClick}>{label}</button>;`,
|
|
91
|
-
`}`,
|
|
92
|
-
``,
|
|
93
|
-
].join('\n');
|
|
76
|
+
function run(cfg, script) {
|
|
77
|
+
return cfg.packageManager === 'npm' ? `npm run ${script}` : `${cfg.packageManager} ${script}`;
|
|
94
78
|
}
|
|
95
79
|
|
|
96
80
|
function readme(cfg) {
|
|
@@ -114,10 +98,17 @@ function readme(cfg) {
|
|
|
114
98
|
'',
|
|
115
99
|
];
|
|
116
100
|
|
|
117
|
-
if (cfg.
|
|
101
|
+
if (cfg.hasApp) {
|
|
102
|
+
lines.push('## Develop', '', '```sh', run(cfg, 'dev') + ' # start the dev server', run(cfg, 'build') + ' # production build', '```', '');
|
|
103
|
+
} else if (cfg.hasLibrary && cfg.isReact) {
|
|
118
104
|
lines.push('## Usage', '', '```' + (cfg.isTs ? 'tsx' : 'jsx'),
|
|
119
|
-
`import { Button } from '${cfg.name}';`, '',
|
|
120
|
-
|
|
105
|
+
`import { Button } from '${cfg.name}';`, '', `<Button label="Click me" />`, '```', '');
|
|
106
|
+
} else if (cfg.hasLibrary && cfg.isVue) {
|
|
107
|
+
lines.push('## Usage', '', '```' + (cfg.isTs ? 'ts' : 'js'),
|
|
108
|
+
`import { Button } from '${cfg.name}';`, '// then <Button label="Click me" /> in your template', '```', '');
|
|
109
|
+
} else if (cfg.hasLibrary && cfg.isSvelte) {
|
|
110
|
+
lines.push('## Usage', '', '```svelte',
|
|
111
|
+
`<script>import { Button } from '${cfg.name}';</script>`, '', `<Button label="Click me" />`, '```', '');
|
|
121
112
|
} else if (cfg.hasLibrary) {
|
|
122
113
|
const imp = cfg.isTs || cfg.hasEsm ? `import { greet } from '${cfg.name}';` : `const { greet } = require('${cfg.name}');`;
|
|
123
114
|
lines.push('## Usage', '', '```' + (cfg.isTs ? 'ts' : 'js'), imp, '', `greet('world'); // "Hello, world!"`, '```', '');
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Storybook for component libraries (React / Vue / Svelte), on the Vite builder.
|
|
2
|
+
|
|
3
|
+
const FW = {
|
|
4
|
+
react: { builder: '@storybook/react-vite', renderer: '@storybook/react', storyExt: 'tsx' },
|
|
5
|
+
vue: { builder: '@storybook/vue3-vite', renderer: '@storybook/vue3', storyExt: 'ts' },
|
|
6
|
+
svelte: { builder: '@storybook/svelte-vite', renderer: '@storybook/svelte', storyExt: 'ts' },
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
id: 'storybook',
|
|
11
|
+
active: (cfg) => cfg.storybook,
|
|
12
|
+
apply(cfg) {
|
|
13
|
+
const fw = FW[cfg.framework];
|
|
14
|
+
const files = {};
|
|
15
|
+
|
|
16
|
+
files['.storybook/main.ts'] = [
|
|
17
|
+
`import type { StorybookConfig } from '${fw.builder}';`,
|
|
18
|
+
``,
|
|
19
|
+
`const config: StorybookConfig = {`,
|
|
20
|
+
`\tstories: ['../src/**/*.stories.@(ts|tsx|svelte)'],`,
|
|
21
|
+
`\taddons: ['@storybook/addon-essentials'],`,
|
|
22
|
+
`\tframework: '${fw.builder}',`,
|
|
23
|
+
`};`,
|
|
24
|
+
`export default config;`,
|
|
25
|
+
``,
|
|
26
|
+
].join('\n');
|
|
27
|
+
|
|
28
|
+
files['.storybook/preview.ts'] = `export default { parameters: {} };\n`;
|
|
29
|
+
|
|
30
|
+
files[`src/Button.stories.${fw.storyExt}`] = story(cfg, fw);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
files,
|
|
34
|
+
pkg: {
|
|
35
|
+
scripts: {
|
|
36
|
+
storybook: 'storybook dev -p 6006',
|
|
37
|
+
'build-storybook': 'storybook build',
|
|
38
|
+
},
|
|
39
|
+
devDependencies: {
|
|
40
|
+
storybook: '^8.2.0',
|
|
41
|
+
[fw.builder]: '^8.2.0',
|
|
42
|
+
[fw.renderer]: '^8.2.0',
|
|
43
|
+
'@storybook/addon-essentials': '^8.2.0',
|
|
44
|
+
vite: '^5.4.0',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function story(cfg, fw) {
|
|
52
|
+
if (cfg.isReact) {
|
|
53
|
+
return [
|
|
54
|
+
`import type { Meta, StoryObj } from '${fw.renderer}';`,
|
|
55
|
+
`import { Button } from './index';`,
|
|
56
|
+
``,
|
|
57
|
+
`const meta: Meta<typeof Button> = { component: Button };`,
|
|
58
|
+
`export default meta;`,
|
|
59
|
+
``,
|
|
60
|
+
`export const Default: StoryObj<typeof Button> = { args: { label: 'Click me' } };`,
|
|
61
|
+
``,
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
if (cfg.isVue) {
|
|
65
|
+
return [
|
|
66
|
+
`import type { Meta, StoryObj } from '${fw.renderer}';`,
|
|
67
|
+
`import { Button } from './index';`,
|
|
68
|
+
``,
|
|
69
|
+
`const meta = { component: Button } satisfies Meta<typeof Button>;`,
|
|
70
|
+
`export default meta;`,
|
|
71
|
+
``,
|
|
72
|
+
`export const Default: StoryObj<typeof meta> = { args: { label: 'Click me' } };`,
|
|
73
|
+
``,
|
|
74
|
+
].join('\n');
|
|
75
|
+
}
|
|
76
|
+
// svelte
|
|
77
|
+
return [
|
|
78
|
+
`import type { Meta, StoryObj } from '${fw.renderer}';`,
|
|
79
|
+
`import Button from './Button.svelte';`,
|
|
80
|
+
``,
|
|
81
|
+
`const meta = { component: Button } satisfies Meta<Button>;`,
|
|
82
|
+
`export default meta;`,
|
|
83
|
+
``,
|
|
84
|
+
`export const Default: StoryObj<typeof meta> = { args: { label: 'Click me' } };`,
|
|
85
|
+
``,
|
|
86
|
+
].join('\n');
|
|
87
|
+
}
|
|
@@ -11,13 +11,23 @@ export default {
|
|
|
11
11
|
const testExt = cfg.isReact ? cfg.srcExt : ext;
|
|
12
12
|
|
|
13
13
|
if (cfg.test === 'vitest') {
|
|
14
|
+
const fw = cfg.isVue
|
|
15
|
+
? { imp: `import vue from '@vitejs/plugin-vue';`, call: 'vue()' }
|
|
16
|
+
: cfg.isSvelte
|
|
17
|
+
? {
|
|
18
|
+
imp: `import { svelte } from '@sveltejs/vite-plugin-svelte';\nimport { svelteTesting } from '@testing-library/svelte/vite';`,
|
|
19
|
+
call: 'svelte(), svelteTesting()',
|
|
20
|
+
}
|
|
21
|
+
: null;
|
|
14
22
|
files[`vitest.config.${ext}`] = [
|
|
23
|
+
fw ? fw.imp : null,
|
|
15
24
|
`import { defineConfig } from 'vitest/config';`,
|
|
16
25
|
``,
|
|
17
26
|
`export default defineConfig({`,
|
|
27
|
+
fw ? `\tplugins: [${fw.call}],` : null,
|
|
18
28
|
`\ttest: {`,
|
|
19
|
-
cfg.
|
|
20
|
-
cfg.
|
|
29
|
+
cfg.hasFramework ? `\t\tenvironment: 'jsdom',` : null,
|
|
30
|
+
cfg.hasFramework ? `\t\tglobals: true,` : null,
|
|
21
31
|
cfg.coverage ? `\t\tcoverage: { provider: 'v8', reporter: ['text', 'lcov'] },` : null,
|
|
22
32
|
`\t},`,
|
|
23
33
|
`});`,
|
|
@@ -26,10 +36,12 @@ export default {
|
|
|
26
36
|
pkg.scripts.test = 'vitest run';
|
|
27
37
|
pkg.scripts['test:watch'] = 'vitest';
|
|
28
38
|
pkg.devDependencies.vitest = '^2.0.0';
|
|
29
|
-
if (cfg.
|
|
39
|
+
if (cfg.hasFramework) {
|
|
30
40
|
pkg.devDependencies.jsdom = '^25.0.0';
|
|
31
|
-
pkg.devDependencies['@testing-library/react'] = '^16.0.0';
|
|
32
41
|
pkg.devDependencies['@testing-library/dom'] = '^10.0.0';
|
|
42
|
+
if (cfg.isReact) pkg.devDependencies['@testing-library/react'] = '^16.0.0';
|
|
43
|
+
if (cfg.isVue) pkg.devDependencies['@testing-library/vue'] = '^8.1.0';
|
|
44
|
+
if (cfg.isSvelte) pkg.devDependencies['@testing-library/svelte'] = '^5.2.0';
|
|
33
45
|
}
|
|
34
46
|
if (cfg.coverage) {
|
|
35
47
|
pkg.scripts.coverage = 'vitest run --coverage';
|
|
@@ -79,16 +91,32 @@ function exampleTest(runner, cfg) {
|
|
|
79
91
|
``,
|
|
80
92
|
].join('\n');
|
|
81
93
|
}
|
|
82
|
-
if (cfg.
|
|
94
|
+
if (cfg.hasFramework) {
|
|
83
95
|
const api = runner === 'jest' ? '' : `import { describe, it, expect } from 'vitest';\n`;
|
|
96
|
+
const app = cfg.hasApp;
|
|
97
|
+
const label = app ? '/Hello from/' : `'Click me'`;
|
|
98
|
+
let lib, importLine, renderCall;
|
|
99
|
+
if (cfg.isReact) {
|
|
100
|
+
lib = '@testing-library/react';
|
|
101
|
+
importLine = app ? `import { App } from './App.js';` : `import { Button } from './index.js';`;
|
|
102
|
+
renderCall = app ? `render(<App />)` : `render(<Button label="Click me" />)`;
|
|
103
|
+
} else if (cfg.isVue) {
|
|
104
|
+
lib = '@testing-library/vue';
|
|
105
|
+
importLine = app ? `import App from './App.vue';` : `import { Button } from './index.js';`;
|
|
106
|
+
renderCall = app ? `render(App)` : `render(Button, { props: { label: 'Click me' } })`;
|
|
107
|
+
} else {
|
|
108
|
+
lib = '@testing-library/svelte';
|
|
109
|
+
importLine = app ? `import App from './App.svelte';` : `import Button from './Button.svelte';`;
|
|
110
|
+
renderCall = app ? `render(App)` : `render(Button, { props: { label: 'Click me' } })`;
|
|
111
|
+
}
|
|
84
112
|
return [
|
|
85
|
-
api + `import { render, screen } from '
|
|
86
|
-
|
|
113
|
+
api + `import { render, screen } from '${lib}';`,
|
|
114
|
+
importLine,
|
|
87
115
|
``,
|
|
88
|
-
`describe('Button', () => {`,
|
|
89
|
-
`\tit('renders
|
|
90
|
-
`\t\
|
|
91
|
-
`\t\texpect(screen.getByText(
|
|
116
|
+
`describe('${app ? 'App' : 'Button'}', () => {`,
|
|
117
|
+
`\tit('renders', () => {`,
|
|
118
|
+
`\t\t${renderCall};`,
|
|
119
|
+
`\t\texpect(screen.getByText(${label})).toBeDefined();`,
|
|
92
120
|
`\t});`,
|
|
93
121
|
`});`,
|
|
94
122
|
``,
|
|
@@ -4,19 +4,20 @@ export default {
|
|
|
4
4
|
id: 'typescript',
|
|
5
5
|
active: (cfg) => cfg.isTs,
|
|
6
6
|
apply(cfg) {
|
|
7
|
-
const noBuild = cfg.bundler === 'none';
|
|
7
|
+
const noBuild = cfg.bundler === 'none' && !cfg.customBuild;
|
|
8
|
+
const webLibs = cfg.hasFramework || cfg.hasApp;
|
|
8
9
|
const compilerOptions = {
|
|
9
10
|
target: 'ES2022',
|
|
10
11
|
module: 'ESNext',
|
|
11
12
|
moduleResolution: 'Bundler',
|
|
12
|
-
lib:
|
|
13
|
+
lib: webLibs ? ['ES2022', 'DOM', 'DOM.Iterable'] : ['ES2022'],
|
|
13
14
|
...(cfg.isReact ? { jsx: 'react-jsx' } : {}),
|
|
14
15
|
strict: true,
|
|
15
16
|
noUncheckedIndexedAccess: true,
|
|
16
17
|
esModuleInterop: true,
|
|
17
18
|
skipLibCheck: true,
|
|
18
19
|
forceConsistentCasingInFileNames: true,
|
|
19
|
-
verbatimModuleSyntax: cfg.bundler !== 'none',
|
|
20
|
+
verbatimModuleSyntax: cfg.bundler !== 'none' && !cfg.hasFramework,
|
|
20
21
|
declaration: true,
|
|
21
22
|
};
|
|
22
23
|
if (noBuild) {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Vite build path: powers Vite SPA apps (React/Vue/Svelte) and Vue component
|
|
2
|
+
// libraries (SFCs need a real compiler). Owns the vite config, build scripts,
|
|
3
|
+
// and — for libraries — the package entry points. index.html + source come
|
|
4
|
+
// from frameworks.js. Svelte libraries ship source and don't come through here.
|
|
5
|
+
|
|
6
|
+
const PLUGIN = {
|
|
7
|
+
react: { import: `import react from '@vitejs/plugin-react';`, call: 'react()', dep: { '@vitejs/plugin-react': '^4.3.0' } },
|
|
8
|
+
vue: { import: `import vue from '@vitejs/plugin-vue';`, call: 'vue()', dep: { '@vitejs/plugin-vue': '^5.1.0' } },
|
|
9
|
+
svelte: { import: `import { svelte } from '@sveltejs/vite-plugin-svelte';`, call: 'svelte()', dep: { '@sveltejs/vite-plugin-svelte': '^4.0.0' } },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
id: 'vite',
|
|
14
|
+
active: (cfg) => cfg.viteBuild,
|
|
15
|
+
apply(cfg) {
|
|
16
|
+
const files = {};
|
|
17
|
+
const pkg = { scripts: {}, devDependencies: { vite: '^5.4.0' } };
|
|
18
|
+
const p = PLUGIN[cfg.framework];
|
|
19
|
+
Object.assign(pkg.devDependencies, p.dep);
|
|
20
|
+
if (cfg.isVue && cfg.isTs) pkg.devDependencies['vue-tsc'] = '^2.0.0';
|
|
21
|
+
|
|
22
|
+
if (cfg.hasApp) {
|
|
23
|
+
// Front-end SPA — not a published package.
|
|
24
|
+
files[`vite.config.${cfg.ext}`] = [p.import, ``, `import { defineConfig } from 'vite';`, ``, `export default defineConfig({`, `\tplugins: [${p.call}],`, `});`, ``].join('\n');
|
|
25
|
+
pkg.private = true;
|
|
26
|
+
pkg.scripts.dev = 'vite';
|
|
27
|
+
// React/Vue get a type-check before build; Svelte types need svelte-check
|
|
28
|
+
// (heavier), so its app just builds.
|
|
29
|
+
const precheck = cfg.isTs && cfg.isReact ? 'tsc --noEmit && ' : cfg.isTs && cfg.isVue ? 'vue-tsc --noEmit && ' : '';
|
|
30
|
+
pkg.scripts.build = precheck + 'vite build';
|
|
31
|
+
pkg.scripts.preview = 'vite preview';
|
|
32
|
+
} else {
|
|
33
|
+
// Vue component library (lib build + declarations).
|
|
34
|
+
files[`vite.config.${cfg.ext}`] = [
|
|
35
|
+
p.import,
|
|
36
|
+
`import dts from 'vite-plugin-dts';`,
|
|
37
|
+
``,
|
|
38
|
+
`import { defineConfig } from 'vite';`,
|
|
39
|
+
``,
|
|
40
|
+
`export default defineConfig({`,
|
|
41
|
+
`\tplugins: [${p.call}, dts({ rollupTypes: true })],`,
|
|
42
|
+
`\tbuild: {`,
|
|
43
|
+
`\t\tlib: { entry: 'src/index.${cfg.ext}', formats: ['es', 'cjs'], fileName: (f) => (f === 'es' ? 'index.js' : 'index.cjs') },`,
|
|
44
|
+
`\t\trollupOptions: { external: ['vue'] },`,
|
|
45
|
+
`\t},`,
|
|
46
|
+
`});`,
|
|
47
|
+
``,
|
|
48
|
+
].join('\n');
|
|
49
|
+
pkg.scripts.build = 'vite build';
|
|
50
|
+
pkg.scripts.dev = 'vite build --watch';
|
|
51
|
+
pkg.devDependencies['vite-plugin-dts'] = '^4.0.0';
|
|
52
|
+
if (cfg.isVue) pkg.devDependencies['vue-tsc'] = '^2.0.0';
|
|
53
|
+
// entry points
|
|
54
|
+
pkg.files = ['dist'];
|
|
55
|
+
pkg.type = 'module';
|
|
56
|
+
pkg.main = './dist/index.cjs';
|
|
57
|
+
pkg.module = './dist/index.js';
|
|
58
|
+
pkg.types = './dist/index.d.ts';
|
|
59
|
+
pkg.exports = { '.': { types: './dist/index.d.ts', import: './dist/index.js', require: './dist/index.cjs' } };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pkg.scripts.clean = 'rimraf dist';
|
|
63
|
+
pkg.devDependencies.rimraf = '^6.0.0';
|
|
64
|
+
return { files, pkg };
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -72,7 +72,7 @@ function ciWorkflow(cfg, codecov) {
|
|
|
72
72
|
if (cfg.isTs) jobs.push(` - run: ${pmRun(cfg, 'typecheck')}`);
|
|
73
73
|
if (cfg.lint !== 'none') jobs.push(` - run: ${pmRun(cfg, 'lint')}`);
|
|
74
74
|
if (cfg.test !== 'none') jobs.push(` - run: ${pmRun(cfg, codecov ? 'coverage' : 'test')}`);
|
|
75
|
-
if (cfg.
|
|
75
|
+
if (cfg.hasBuild) jobs.push(` - run: ${pmRun(cfg, 'build')}`);
|
|
76
76
|
const cov = codecov
|
|
77
77
|
? '\n - uses: codecov/codecov-action@v4\n with:\n token: ${{ secrets.CODECOV_TOKEN }}'
|
|
78
78
|
: '';
|
|
@@ -131,7 +131,7 @@ function releaseWorkflow(cfg) {
|
|
|
131
131
|
' publish:',
|
|
132
132
|
' runs-on: ubuntu-latest',
|
|
133
133
|
setupSteps(cfg),
|
|
134
|
-
cfg.
|
|
134
|
+
cfg.hasBuild ? ` - run: ${pmRun(cfg, 'build')}` : null,
|
|
135
135
|
' - run: npm publish --provenance --access public',
|
|
136
136
|
' env:',
|
|
137
137
|
' NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}',
|
package/src/core/options.js
CHANGED
|
@@ -34,13 +34,16 @@ export const OPTIONS = {
|
|
|
34
34
|
{ value: 'library', label: 'Library (importable package)' },
|
|
35
35
|
{ value: 'cli', label: 'CLI tool (ships a bin)' },
|
|
36
36
|
{ value: 'service', label: 'HTTP service (Hono)' },
|
|
37
|
+
{ value: 'app', label: 'App (Vite SPA)' },
|
|
37
38
|
],
|
|
38
39
|
},
|
|
39
40
|
framework: {
|
|
40
41
|
group: 'core', type: 'select', label: 'Framework', default: 'none',
|
|
41
42
|
choices: [
|
|
42
43
|
{ value: 'none', label: 'None (plain package)' },
|
|
43
|
-
{ value: 'react', label: 'React
|
|
44
|
+
{ value: 'react', label: 'React' },
|
|
45
|
+
{ value: 'vue', label: 'Vue' },
|
|
46
|
+
{ value: 'svelte', label: 'Svelte' },
|
|
44
47
|
],
|
|
45
48
|
},
|
|
46
49
|
packageManager: {
|
|
@@ -87,6 +90,7 @@ export const OPTIONS = {
|
|
|
87
90
|
],
|
|
88
91
|
},
|
|
89
92
|
coverage: { group: 'quality', type: 'boolean', label: 'Coverage reporting', default: true },
|
|
93
|
+
storybook: { group: 'quality', type: 'boolean', label: 'Storybook (component libraries)', default: false },
|
|
90
94
|
|
|
91
95
|
// ---- lint / format ----
|
|
92
96
|
lint: {
|
|
@@ -197,17 +201,40 @@ export function normalizeConfig(input = {}) {
|
|
|
197
201
|
// npm-publish + changesets are complementary; nothing to coerce, just noted.
|
|
198
202
|
|
|
199
203
|
cfg.isReact = cfg.framework === 'react';
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
cfg.isVue = cfg.framework === 'vue';
|
|
205
|
+
cfg.isSvelte = cfg.framework === 'svelte';
|
|
206
|
+
cfg.hasFramework = cfg.framework !== 'none';
|
|
207
|
+
|
|
208
|
+
cfg.hasApp = cfg.target.includes('app');
|
|
209
|
+
// A component-framework package that isn't an app is a library.
|
|
210
|
+
if (cfg.hasFramework && !cfg.hasApp && !cfg.target.includes('library')) {
|
|
211
|
+
cfg.target = ['library', ...cfg.target];
|
|
212
|
+
}
|
|
202
213
|
|
|
203
214
|
cfg.isTs = cfg.language === 'ts';
|
|
204
215
|
cfg.ext = cfg.isTs ? 'ts' : 'js';
|
|
205
|
-
//
|
|
216
|
+
// JSX source extension for React; Vue/Svelte use their own component files.
|
|
206
217
|
cfg.srcExt = cfg.isReact ? (cfg.isTs ? 'tsx' : 'jsx') : cfg.ext;
|
|
218
|
+
|
|
207
219
|
cfg.hasLibrary = cfg.target.includes('library');
|
|
208
220
|
cfg.hasCli = cfg.target.includes('cli');
|
|
209
221
|
cfg.hasService = cfg.target.includes('service');
|
|
222
|
+
|
|
223
|
+
// Vite builds apps and Vue libraries (SFCs); Svelte libraries ship source
|
|
224
|
+
// (no build); React libraries use tsup (JSX is native to esbuild).
|
|
225
|
+
cfg.viteBuild = cfg.hasApp || cfg.isVue;
|
|
226
|
+
cfg.svelteLib = cfg.isSvelte && !cfg.hasApp;
|
|
227
|
+
cfg.customBuild = cfg.viteBuild || cfg.svelteLib; // bundler.js steps aside
|
|
228
|
+
cfg.usesVite = cfg.viteBuild || cfg.isSvelte; // Svelte tests need the vite plugin too
|
|
229
|
+
// Whether a `build` script exists (Svelte libraries ship source, no build).
|
|
230
|
+
cfg.hasBuild = cfg.viteBuild || (!cfg.svelteLib && (cfg.bundler !== 'none' || cfg.isTs));
|
|
231
|
+
|
|
232
|
+
// Apps aren't published packages.
|
|
233
|
+
if (cfg.hasApp) cfg.moduleFormat = 'esm';
|
|
210
234
|
cfg.hasEsm = cfg.moduleFormat === 'esm' || cfg.moduleFormat === 'dual';
|
|
211
235
|
cfg.hasCjs = cfg.moduleFormat === 'cjs' || cfg.moduleFormat === 'dual';
|
|
236
|
+
|
|
237
|
+
// Storybook only applies to component libraries.
|
|
238
|
+
if (!cfg.hasFramework || cfg.hasApp || !cfg.hasLibrary) cfg.storybook = false;
|
|
212
239
|
return cfg;
|
|
213
240
|
}
|
package/src/core/presets.js
CHANGED
|
@@ -8,6 +8,9 @@ export const PRESETS = {
|
|
|
8
8
|
cli: { language: 'ts', target: ['cli', 'library'], moduleFormat: 'esm' },
|
|
9
9
|
'react-lib': { language: 'ts', framework: 'react', target: ['library'], moduleFormat: 'dual', test: 'vitest' },
|
|
10
10
|
'react-lib-js': { language: 'js', framework: 'react', target: ['library'], moduleFormat: 'dual', bundler: 'tsup', test: 'vitest' },
|
|
11
|
+
'react-app': { language: 'ts', framework: 'react', target: ['app'], test: 'vitest', release: 'none', workflows: ['ci'] },
|
|
12
|
+
'vue-lib': { language: 'ts', framework: 'vue', target: ['library'], test: 'vitest' },
|
|
13
|
+
'svelte-lib': { language: 'ts', framework: 'svelte', target: ['library'], test: 'vitest' },
|
|
11
14
|
'node-service': {
|
|
12
15
|
language: 'ts', target: ['service'], moduleFormat: 'esm', bundler: 'tsup',
|
|
13
16
|
test: 'vitest', lint: 'eslint-prettier', gitHooks: 'simple-git-hooks',
|
|
@@ -43,6 +46,9 @@ export const PRESET_INFO = {
|
|
|
43
46
|
cli: 'TypeScript CLI tool — ESM, ships a bin.',
|
|
44
47
|
'react-lib': 'React component library (TS) — JSX, peer deps, jsdom tests.',
|
|
45
48
|
'react-lib-js': 'React component library (JS) — JSX, peer deps, jsdom tests.',
|
|
49
|
+
'react-app': 'React SPA — Vite dev server, build, Testing Library.',
|
|
50
|
+
'vue-lib': 'Vue component library — Vite lib build (SFCs), dual + types.',
|
|
51
|
+
'svelte-lib': 'Svelte component library — ships source, peer svelte, jsdom tests.',
|
|
46
52
|
'node-service': 'Node HTTP service (Hono) — tsx dev, tsup build, Dockerfile.',
|
|
47
53
|
oss: 'Full open-source library — coverage, CodeQL, Codecov, Renovate, Changesets.',
|
|
48
54
|
minimal: 'Bare TS library — tsup only, no tests/lint/CI.',
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// React component-library support: react/react-dom as peers (so consumers bring
|
|
2
|
-
// their own), plus dev copies for building and testing. tsup/rollup externalize
|
|
3
|
-
// peerDependencies automatically, so React is never bundled into your package.
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
id: 'react',
|
|
7
|
-
active: (cfg) => cfg.isReact,
|
|
8
|
-
apply(cfg) {
|
|
9
|
-
const pkg = {
|
|
10
|
-
peerDependencies: {
|
|
11
|
-
react: '>=18',
|
|
12
|
-
'react-dom': '>=18',
|
|
13
|
-
},
|
|
14
|
-
devDependencies: {
|
|
15
|
-
react: '^18.3.0',
|
|
16
|
-
'react-dom': '^18.3.0',
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
if (cfg.isTs) {
|
|
20
|
-
pkg.devDependencies['@types/react'] = '^18.3.0';
|
|
21
|
-
pkg.devDependencies['@types/react-dom'] = '^18.3.0';
|
|
22
|
-
}
|
|
23
|
-
return { files: {}, pkg };
|
|
24
|
-
},
|
|
25
|
-
};
|