create-packkit 3.0.0 → 3.1.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 +16 -0
- package/package.json +7 -2
- package/src/cli/args.js +0 -1
- package/src/cli/index.js +55 -14
- package/src/cli/upgrade.js +147 -0
- package/src/core/features/bundler.js +13 -7
- package/src/core/features/checks.js +4 -3
- package/src/core/features/env.js +2 -1
- package/src/core/features/frameworks.js +15 -14
- package/src/core/features/githooks.js +7 -6
- package/src/core/features/index.js +7 -0
- package/src/core/features/lint.js +7 -6
- package/src/core/features/meta.js +2 -1
- package/src/core/features/release.js +4 -3
- package/src/core/features/service.js +6 -5
- package/src/core/features/sizelimit.js +3 -2
- package/src/core/features/storybook.js +5 -4
- package/src/core/features/test.js +14 -15
- package/src/core/features/typescript.js +2 -1
- package/src/core/features/vite.js +9 -8
- package/src/core/monorepo.js +75 -101
- package/src/core/options.js +23 -0
- package/src/core/versions.js +121 -0
- package/src/embedded/contract.js +8 -6
- package/src/embedded/index.js +63 -8
- package/src/embedded/upgrade.js +148 -0
- package/types/embedded.d.ts +21 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { V } from '../versions.js';
|
|
1
2
|
// HTTP service target. Supports Hono (default), Fastify and Express. Splits the
|
|
2
3
|
// app (testable) from the server entry (which listens), adds start/dev scripts,
|
|
3
4
|
// and a production Dockerfile.
|
|
@@ -24,7 +25,7 @@ export default {
|
|
|
24
25
|
},
|
|
25
26
|
dependencies: deps(fw),
|
|
26
27
|
devDependencies: {
|
|
27
|
-
...(cfg.isTs ? { tsx:
|
|
28
|
+
...(cfg.isTs ? { tsx: V.tsx } : {}),
|
|
28
29
|
...(cfg.isTs ? typeDeps(fw) : {}),
|
|
29
30
|
},
|
|
30
31
|
},
|
|
@@ -33,13 +34,13 @@ export default {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
function deps(fw) {
|
|
36
|
-
if (fw === 'fastify') return { fastify:
|
|
37
|
-
if (fw === 'express') return { express:
|
|
38
|
-
return { hono:
|
|
37
|
+
if (fw === 'fastify') return { fastify: V.fastify };
|
|
38
|
+
if (fw === 'express') return { express: V.express };
|
|
39
|
+
return { hono: V.hono, '@hono/node-server': V['@hono/node-server'] };
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function typeDeps(fw) {
|
|
42
|
-
if (fw === 'express') return { '@types/express': '
|
|
43
|
+
if (fw === 'express') return { '@types/express': V['@types/express'] };
|
|
43
44
|
return {};
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// brotli-compressed size of the built entry and fails when it exceeds the
|
|
3
3
|
// limit — cheap insurance against a dependency silently bloating the package.
|
|
4
4
|
import { toJson } from '../render.js';
|
|
5
|
+
import { V } from '../versions.js';
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
id: 'sizelimit',
|
|
@@ -14,8 +15,8 @@ export default {
|
|
|
14
15
|
pkg: {
|
|
15
16
|
scripts: { size: 'size-limit' },
|
|
16
17
|
devDependencies: {
|
|
17
|
-
'size-limit': '
|
|
18
|
-
'@size-limit/preset-small-lib': '
|
|
18
|
+
'size-limit': V['size-limit'],
|
|
19
|
+
'@size-limit/preset-small-lib': V['@size-limit/preset-small-lib'],
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { V } from '../versions.js';
|
|
1
2
|
// Storybook for component libraries (React / Vue / Svelte), on the Vite builder.
|
|
2
3
|
|
|
3
4
|
const FW = {
|
|
@@ -37,10 +38,10 @@ export default {
|
|
|
37
38
|
'build-storybook': 'storybook build',
|
|
38
39
|
},
|
|
39
40
|
devDependencies: {
|
|
40
|
-
storybook:
|
|
41
|
-
[fw.builder]:
|
|
42
|
-
[fw.renderer]:
|
|
43
|
-
vite:
|
|
41
|
+
storybook: V.storybook,
|
|
42
|
+
[fw.builder]: V[fw.builder],
|
|
43
|
+
[fw.renderer]: V[fw.renderer],
|
|
44
|
+
vite: V.vite,
|
|
44
45
|
},
|
|
45
46
|
},
|
|
46
47
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { V } from '../versions.js';
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
id: 'test',
|
|
@@ -38,40 +38,40 @@ export default {
|
|
|
38
38
|
].filter((l) => l !== null).join('\n');
|
|
39
39
|
pkg.scripts.test = 'vitest run';
|
|
40
40
|
pkg.scripts['test:watch'] = 'vitest';
|
|
41
|
-
pkg.devDependencies.vitest =
|
|
41
|
+
pkg.devDependencies.vitest = V.vitest;
|
|
42
42
|
if (cfg.hasFramework) {
|
|
43
|
-
pkg.devDependencies.jsdom =
|
|
44
|
-
pkg.devDependencies['@testing-library/dom'] = '
|
|
45
|
-
if (cfg.isReact) pkg.devDependencies['@testing-library/react'] = '
|
|
46
|
-
if (cfg.isVue) pkg.devDependencies['@testing-library/vue'] = '
|
|
47
|
-
if (cfg.isSvelte) pkg.devDependencies['@testing-library/svelte'] = '
|
|
43
|
+
pkg.devDependencies.jsdom = V.jsdom;
|
|
44
|
+
pkg.devDependencies['@testing-library/dom'] = V['@testing-library/dom'];
|
|
45
|
+
if (cfg.isReact) pkg.devDependencies['@testing-library/react'] = V['@testing-library/react'];
|
|
46
|
+
if (cfg.isVue) pkg.devDependencies['@testing-library/vue'] = V['@testing-library/vue'];
|
|
47
|
+
if (cfg.isSvelte) pkg.devDependencies['@testing-library/svelte'] = V['@testing-library/svelte'];
|
|
48
48
|
}
|
|
49
49
|
if (cfg.coverage) {
|
|
50
50
|
pkg.scripts.coverage = 'vitest run --coverage';
|
|
51
|
-
pkg.devDependencies['@vitest/coverage-v8'] = '
|
|
51
|
+
pkg.devDependencies['@vitest/coverage-v8'] = V['@vitest/coverage-v8'];
|
|
52
52
|
}
|
|
53
53
|
files[`src/index.test.${testExt}`] = exampleTest('vitest', cfg);
|
|
54
54
|
} else if (cfg.test === 'jest') {
|
|
55
55
|
files['jest.config.js'] = jestConfig(cfg);
|
|
56
56
|
pkg.scripts.test = 'jest';
|
|
57
57
|
pkg.scripts['test:watch'] = 'jest --watch';
|
|
58
|
-
pkg.devDependencies.jest =
|
|
58
|
+
pkg.devDependencies.jest = V.jest;
|
|
59
59
|
if (cfg.isTs) {
|
|
60
|
-
pkg.devDependencies['ts-jest'] = '
|
|
61
|
-
pkg.devDependencies['@types/jest'] = '
|
|
60
|
+
pkg.devDependencies['ts-jest'] = V['ts-jest'];
|
|
61
|
+
pkg.devDependencies['@types/jest'] = V['@types/jest'];
|
|
62
62
|
}
|
|
63
63
|
if (cfg.coverage) pkg.scripts.coverage = 'jest --coverage';
|
|
64
64
|
files[`src/index.test.${testExt}`] = exampleTest('jest', cfg);
|
|
65
65
|
} else if (cfg.test === 'node') {
|
|
66
66
|
pkg.scripts.test = cfg.isTs ? 'node --import tsx --test "src/**/*.test.ts"' : 'node --test';
|
|
67
|
-
if (cfg.isTs) pkg.devDependencies.tsx =
|
|
67
|
+
if (cfg.isTs) pkg.devDependencies.tsx = V.tsx;
|
|
68
68
|
files[`src/index.test.${testExt}`] = exampleTest('node', cfg);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Express services test through supertest (no built-in inject).
|
|
72
72
|
if (cfg.hasService && cfg.serviceFramework === 'express') {
|
|
73
|
-
pkg.devDependencies.supertest =
|
|
74
|
-
if (cfg.isTs) pkg.devDependencies['@types/supertest'] = '
|
|
73
|
+
pkg.devDependencies.supertest = V.supertest;
|
|
74
|
+
if (cfg.isTs) pkg.devDependencies['@types/supertest'] = V['@types/supertest'];
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
return { files, pkg };
|
|
@@ -159,7 +159,6 @@ function exampleTest(runner, cfg) {
|
|
|
159
159
|
].join('\n');
|
|
160
160
|
}
|
|
161
161
|
const api = runner === 'jest' ? `` : `import { describe, it, expect } from 'vitest';\n`;
|
|
162
|
-
const expectApi = runner === 'jest' ? '' : '';
|
|
163
162
|
return [
|
|
164
163
|
api + `import { greet } from '${imp}';`,
|
|
165
164
|
``,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toJson } from '../render.js';
|
|
2
|
+
import { V } from '../versions.js';
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
id: 'typescript',
|
|
@@ -45,7 +46,7 @@ export default {
|
|
|
45
46
|
},
|
|
46
47
|
pkg: {
|
|
47
48
|
devDependencies: {
|
|
48
|
-
typescript:
|
|
49
|
+
typescript: V.typescript,
|
|
49
50
|
'@types/node': `^${cfg.nodeVersion}.0.0`,
|
|
50
51
|
},
|
|
51
52
|
},
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { V } from '../versions.js';
|
|
1
2
|
// Vite build path: powers Vite SPA apps (React/Vue/Svelte) and Vue component
|
|
2
3
|
// libraries (SFCs need a real compiler). Owns the vite config, build scripts,
|
|
3
4
|
// and — for libraries — the package entry points. index.html + source come
|
|
4
5
|
// from frameworks.js. Svelte libraries ship source and don't come through here.
|
|
5
6
|
|
|
6
7
|
const PLUGIN = {
|
|
7
|
-
react: { import: `import react from '@vitejs/plugin-react';`, call: 'react()', dep: { '@vitejs/plugin-react': '
|
|
8
|
-
vue: { import: `import vue from '@vitejs/plugin-vue';`, call: 'vue()', dep: { '@vitejs/plugin-vue': '
|
|
9
|
-
svelte: { import: `import { svelte } from '@sveltejs/vite-plugin-svelte';`, call: 'svelte()', dep: { '@sveltejs/vite-plugin-svelte': '
|
|
8
|
+
react: { import: `import react from '@vitejs/plugin-react';`, call: 'react()', dep: { '@vitejs/plugin-react': V['@vitejs/plugin-react'] } },
|
|
9
|
+
vue: { import: `import vue from '@vitejs/plugin-vue';`, call: 'vue()', dep: { '@vitejs/plugin-vue': V['@vitejs/plugin-vue'] } },
|
|
10
|
+
svelte: { import: `import { svelte } from '@sveltejs/vite-plugin-svelte';`, call: 'svelte()', dep: { '@sveltejs/vite-plugin-svelte': V['@sveltejs/vite-plugin-svelte'] } },
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export default {
|
|
@@ -14,10 +15,10 @@ export default {
|
|
|
14
15
|
active: (cfg) => cfg.viteBuild,
|
|
15
16
|
apply(cfg) {
|
|
16
17
|
const files = {};
|
|
17
|
-
const pkg = { scripts: {}, devDependencies: { vite:
|
|
18
|
+
const pkg = { scripts: {}, devDependencies: { vite: V.vite } };
|
|
18
19
|
const p = PLUGIN[cfg.framework];
|
|
19
20
|
Object.assign(pkg.devDependencies, p.dep);
|
|
20
|
-
if (cfg.isVue && cfg.isTs) pkg.devDependencies['vue-tsc'] = '
|
|
21
|
+
if (cfg.isVue && cfg.isTs) pkg.devDependencies['vue-tsc'] = V['vue-tsc'];
|
|
21
22
|
|
|
22
23
|
if (cfg.hasApp) {
|
|
23
24
|
// Front-end SPA — not a published package.
|
|
@@ -48,8 +49,8 @@ export default {
|
|
|
48
49
|
].join('\n');
|
|
49
50
|
pkg.scripts.build = 'vite build';
|
|
50
51
|
pkg.scripts.dev = 'vite build --watch';
|
|
51
|
-
pkg.devDependencies['vite-plugin-dts'] = '
|
|
52
|
-
if (cfg.isVue) pkg.devDependencies['vue-tsc'] = '
|
|
52
|
+
pkg.devDependencies['vite-plugin-dts'] = V['vite-plugin-dts'];
|
|
53
|
+
if (cfg.isVue) pkg.devDependencies['vue-tsc'] = V['vue-tsc'];
|
|
53
54
|
// entry points
|
|
54
55
|
pkg.files = ['dist'];
|
|
55
56
|
pkg.type = 'module';
|
|
@@ -60,7 +61,7 @@ export default {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
pkg.scripts.clean = 'rimraf dist';
|
|
63
|
-
pkg.devDependencies.rimraf =
|
|
64
|
+
pkg.devDependencies.rimraf = V.rimraf;
|
|
64
65
|
return { files, pkg };
|
|
65
66
|
},
|
|
66
67
|
};
|
package/src/core/monorepo.js
CHANGED
|
@@ -7,6 +7,7 @@ import community from './features/community.js';
|
|
|
7
7
|
import agents from './features/agents.js';
|
|
8
8
|
import gitfiles from './features/gitfiles.js';
|
|
9
9
|
import { provenance } from './provenance.js';
|
|
10
|
+
import { V } from './versions.js';
|
|
10
11
|
|
|
11
12
|
export function buildMonorepo(cfg) {
|
|
12
13
|
// Two genuinely different shapes: a set of publishable libraries, or a
|
|
@@ -22,10 +23,7 @@ export function buildMonorepo(cfg) {
|
|
|
22
23
|
const utils = `@${scope}/utils`;
|
|
23
24
|
const wsProto = pm === 'pnpm' ? 'workspace:*' : '*';
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
for (const feat of [community, agents, gitfiles]) {
|
|
27
|
-
if (feat.active(cfg)) Object.assign(files, feat.apply(cfg).files);
|
|
28
|
-
}
|
|
26
|
+
Object.assign(files, workspaceScaffold(cfg, { workspaceGlobs: ['packages/*'] }));
|
|
29
27
|
|
|
30
28
|
// ---- root ----
|
|
31
29
|
const rootPkg = {
|
|
@@ -46,22 +44,20 @@ export function buildMonorepo(cfg) {
|
|
|
46
44
|
release: 'turbo build && changeset publish',
|
|
47
45
|
},
|
|
48
46
|
devDependencies: {
|
|
49
|
-
turbo:
|
|
50
|
-
typescript:
|
|
51
|
-
tsup:
|
|
52
|
-
vitest:
|
|
53
|
-
eslint:
|
|
54
|
-
'@eslint/js': '
|
|
55
|
-
'typescript-eslint': '
|
|
56
|
-
prettier:
|
|
57
|
-
'@changesets/cli': '
|
|
47
|
+
turbo: V.turbo,
|
|
48
|
+
typescript: V.typescript,
|
|
49
|
+
tsup: V.tsup,
|
|
50
|
+
vitest: V.vitest,
|
|
51
|
+
eslint: V.eslint,
|
|
52
|
+
'@eslint/js': V['@eslint/js'],
|
|
53
|
+
'typescript-eslint': V['typescript-eslint'],
|
|
54
|
+
prettier: V.prettier,
|
|
55
|
+
'@changesets/cli': V['@changesets/cli'],
|
|
58
56
|
'@types/node': `^${cfg.nodeVersion}.0.0`,
|
|
59
57
|
},
|
|
60
58
|
};
|
|
61
59
|
files['package.json'] = toJson(rootPkg);
|
|
62
60
|
|
|
63
|
-
if (pm === 'pnpm') files['pnpm-workspace.yaml'] = 'packages:\n - "packages/*"\n';
|
|
64
|
-
|
|
65
61
|
files['turbo.json'] = toJson({
|
|
66
62
|
$schema: 'https://turbo.build/schema.json',
|
|
67
63
|
tasks: {
|
|
@@ -73,21 +69,6 @@ export function buildMonorepo(cfg) {
|
|
|
73
69
|
},
|
|
74
70
|
});
|
|
75
71
|
|
|
76
|
-
files['tsconfig.base.json'] = toJson({
|
|
77
|
-
$schema: 'https://json.schemastore.org/tsconfig',
|
|
78
|
-
compilerOptions: {
|
|
79
|
-
target: 'ES2022',
|
|
80
|
-
module: 'ESNext',
|
|
81
|
-
moduleResolution: 'Bundler',
|
|
82
|
-
lib: ['ES2022'],
|
|
83
|
-
strict: true,
|
|
84
|
-
esModuleInterop: true,
|
|
85
|
-
skipLibCheck: true,
|
|
86
|
-
declaration: true,
|
|
87
|
-
noEmit: true,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
|
|
91
72
|
files['.changeset/config.json'] = toJson({
|
|
92
73
|
$schema: 'https://unpkg.com/@changesets/config@3.0.0/schema.json',
|
|
93
74
|
changelog: '@changesets/cli/changelog',
|
|
@@ -97,22 +78,7 @@ export function buildMonorepo(cfg) {
|
|
|
97
78
|
});
|
|
98
79
|
files['.changeset/README.md'] = '# Changesets\n\nRun `npx changeset` to record a version bump for your next release.\n';
|
|
99
80
|
|
|
100
|
-
files['eslint.config.js'] = [
|
|
101
|
-
`import js from '@eslint/js';`,
|
|
102
|
-
`import tseslint from 'typescript-eslint';`,
|
|
103
|
-
``,
|
|
104
|
-
`export default tseslint.config(`,
|
|
105
|
-
`\tjs.configs.recommended,`,
|
|
106
|
-
`\t...tseslint.configs.recommended,`,
|
|
107
|
-
`\t{ ignores: ['**/dist'] },`,
|
|
108
|
-
`);`,
|
|
109
|
-
``,
|
|
110
|
-
].join('\n');
|
|
111
|
-
files['.prettierrc.json'] = toJson({ useTabs: true, singleQuote: true, semi: true, printWidth: 100, trailingComma: 'all' });
|
|
112
|
-
|
|
113
81
|
files['README.md'] = rootReadme(cfg, pm, core, utils);
|
|
114
|
-
files['packkit.json'] = provenance(cfg);
|
|
115
|
-
files['.github/workflows/ci.yml'] = ciWorkflow(cfg, pm);
|
|
116
82
|
|
|
117
83
|
// ---- packages ----
|
|
118
84
|
addPackage(files, {
|
|
@@ -145,7 +111,6 @@ export function buildMonorepo(cfg) {
|
|
|
145
111
|
deps: { [core]: wsProto },
|
|
146
112
|
});
|
|
147
113
|
|
|
148
|
-
const install = pm === 'npm' ? 'npm install' : `${pm} install`;
|
|
149
114
|
return {
|
|
150
115
|
config: cfg,
|
|
151
116
|
files,
|
|
@@ -173,11 +138,9 @@ function buildFullstack(cfg) {
|
|
|
173
138
|
const scope = cfg.name.replace(/^@/, '').split('/')[0];
|
|
174
139
|
const shared = `@${scope}/shared`;
|
|
175
140
|
const wsProto = pm === 'pnpm' ? 'workspace:*' : '*';
|
|
176
|
-
const run = (s) => (pm === 'npm' ? `npm run ${s}` : `${pm} ${s}`);
|
|
177
141
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
142
|
+
// apps/* + packages/*, and the DOM lib the web app's JSX needs.
|
|
143
|
+
Object.assign(files, workspaceScaffold(cfg, { workspaceGlobs: ['apps/*', 'packages/*'], tsconfigLib: ['ES2022', 'DOM'] }));
|
|
181
144
|
|
|
182
145
|
files['package.json'] = toJson({
|
|
183
146
|
name: cfg.name,
|
|
@@ -198,21 +161,17 @@ function buildFullstack(cfg) {
|
|
|
198
161
|
typecheck: 'turbo typecheck',
|
|
199
162
|
},
|
|
200
163
|
devDependencies: {
|
|
201
|
-
turbo:
|
|
202
|
-
typescript:
|
|
203
|
-
vitest:
|
|
204
|
-
eslint:
|
|
205
|
-
'@eslint/js': '
|
|
206
|
-
'typescript-eslint': '
|
|
207
|
-
prettier:
|
|
164
|
+
turbo: V.turbo,
|
|
165
|
+
typescript: V.typescript,
|
|
166
|
+
vitest: V.vitest,
|
|
167
|
+
eslint: V.eslint,
|
|
168
|
+
'@eslint/js': V['@eslint/js'],
|
|
169
|
+
'typescript-eslint': V['typescript-eslint'],
|
|
170
|
+
prettier: V.prettier,
|
|
208
171
|
'@types/node': `^${cfg.nodeVersion}.0.0`,
|
|
209
172
|
},
|
|
210
173
|
});
|
|
211
174
|
|
|
212
|
-
if (pm === 'pnpm') {
|
|
213
|
-
files['pnpm-workspace.yaml'] = 'packages:\n - "apps/*"\n - "packages/*"\n';
|
|
214
|
-
}
|
|
215
|
-
|
|
216
175
|
files['turbo.json'] = toJson({
|
|
217
176
|
$schema: 'https://turbo.build/schema.json',
|
|
218
177
|
tasks: {
|
|
@@ -226,36 +185,7 @@ function buildFullstack(cfg) {
|
|
|
226
185
|
},
|
|
227
186
|
});
|
|
228
187
|
|
|
229
|
-
files['tsconfig.base.json'] = toJson({
|
|
230
|
-
$schema: 'https://json.schemastore.org/tsconfig',
|
|
231
|
-
compilerOptions: {
|
|
232
|
-
target: 'ES2022',
|
|
233
|
-
module: 'ESNext',
|
|
234
|
-
moduleResolution: 'Bundler',
|
|
235
|
-
lib: ['ES2022', 'DOM'],
|
|
236
|
-
strict: true,
|
|
237
|
-
esModuleInterop: true,
|
|
238
|
-
skipLibCheck: true,
|
|
239
|
-
declaration: true,
|
|
240
|
-
noEmit: true,
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
files['eslint.config.js'] = [
|
|
245
|
-
`import js from '@eslint/js';`,
|
|
246
|
-
`import tseslint from 'typescript-eslint';`,
|
|
247
|
-
``,
|
|
248
|
-
`export default tseslint.config(`,
|
|
249
|
-
`\tjs.configs.recommended,`,
|
|
250
|
-
`\t...tseslint.configs.recommended,`,
|
|
251
|
-
`\t{ ignores: ['**/dist'] },`,
|
|
252
|
-
`);`,
|
|
253
|
-
``,
|
|
254
|
-
].join('\n');
|
|
255
|
-
files['.prettierrc.json'] = toJson({ useTabs: true, singleQuote: true, semi: true, printWidth: 100, trailingComma: 'all' });
|
|
256
|
-
files['.github/workflows/ci.yml'] = ciWorkflow(cfg, pm);
|
|
257
188
|
files['README.md'] = fullstackReadme(cfg, pm, shared);
|
|
258
|
-
files['packkit.json'] = provenance(cfg);
|
|
259
189
|
|
|
260
190
|
// ---- packages/shared: the contract both sides compile against ----
|
|
261
191
|
files['packages/shared/package.json'] = toJson({
|
|
@@ -272,7 +202,7 @@ function buildFullstack(cfg) {
|
|
|
272
202
|
typecheck: 'tsc --noEmit',
|
|
273
203
|
lint: 'eslint .',
|
|
274
204
|
},
|
|
275
|
-
devDependencies: { tsup:
|
|
205
|
+
devDependencies: { tsup: V.tsup },
|
|
276
206
|
});
|
|
277
207
|
files['packages/shared/tsconfig.json'] = toJson({ extends: '../../tsconfig.base.json', include: ['src'] });
|
|
278
208
|
files['packages/shared/src/index.ts'] = [
|
|
@@ -307,8 +237,8 @@ function buildFullstack(cfg) {
|
|
|
307
237
|
typecheck: 'tsc --noEmit',
|
|
308
238
|
lint: 'eslint .',
|
|
309
239
|
},
|
|
310
|
-
dependencies: { hono:
|
|
311
|
-
devDependencies: { tsx:
|
|
240
|
+
dependencies: { hono: V.hono, '@hono/node-server': V['@hono/node-server'], [shared]: wsProto },
|
|
241
|
+
devDependencies: { tsx: V.tsx, tsup: V.tsup },
|
|
312
242
|
});
|
|
313
243
|
files['apps/server/tsconfig.json'] = toJson({ extends: '../../tsconfig.base.json', include: ['src'] });
|
|
314
244
|
files['apps/server/src/app.ts'] = [
|
|
@@ -368,17 +298,17 @@ function buildFullstack(cfg) {
|
|
|
368
298
|
typecheck: 'tsc --noEmit',
|
|
369
299
|
lint: 'eslint .',
|
|
370
300
|
},
|
|
371
|
-
dependencies: { react:
|
|
301
|
+
dependencies: { react: V.react, 'react-dom': V['react-dom'], [shared]: wsProto },
|
|
372
302
|
devDependencies: {
|
|
373
|
-
vite:
|
|
374
|
-
'@vitejs/plugin-react': '
|
|
303
|
+
vite: V.vite,
|
|
304
|
+
'@vitejs/plugin-react': V['@vitejs/plugin-react'],
|
|
375
305
|
// Without the React types, `turbo typecheck` fails on the very first run
|
|
376
306
|
// with TS7016/TS7026 on every JSX element.
|
|
377
|
-
'@types/react': '
|
|
378
|
-
'@types/react-dom': '
|
|
379
|
-
'@testing-library/react': '
|
|
380
|
-
'@testing-library/dom': '
|
|
381
|
-
jsdom:
|
|
307
|
+
'@types/react': V['@types/react'],
|
|
308
|
+
'@types/react-dom': V['@types/react-dom'],
|
|
309
|
+
'@testing-library/react': V['@testing-library/react'],
|
|
310
|
+
'@testing-library/dom': V['@testing-library/dom'],
|
|
311
|
+
jsdom: V.jsdom,
|
|
382
312
|
},
|
|
383
313
|
});
|
|
384
314
|
files['apps/web/tsconfig.json'] = toJson({
|
|
@@ -515,6 +445,50 @@ function fullstackReadme(cfg, pm, shared) {
|
|
|
515
445
|
].join('\n');
|
|
516
446
|
}
|
|
517
447
|
|
|
448
|
+
// Root-level scaffolding shared by both monorepo layouts: workspace globs, the
|
|
449
|
+
// TypeScript base config, lint/format config, community files, CI, and
|
|
450
|
+
// provenance. Each layout adds its own root package.json, turbo tasks, and
|
|
451
|
+
// packages on top. Extracted so these aren't maintained twice.
|
|
452
|
+
function workspaceScaffold(cfg, { workspaceGlobs, tsconfigLib = ['ES2022'] }) {
|
|
453
|
+
const files = {};
|
|
454
|
+
// Reuse the package-agnostic root files (LICENSE, community, AGENTS.md, gitignore).
|
|
455
|
+
for (const feat of [community, agents, gitfiles]) {
|
|
456
|
+
if (feat.active(cfg)) Object.assign(files, feat.apply(cfg).files);
|
|
457
|
+
}
|
|
458
|
+
if (cfg.packageManager === 'pnpm') {
|
|
459
|
+
files['pnpm-workspace.yaml'] = 'packages:\n' + workspaceGlobs.map((g) => ` - "${g}"\n`).join('');
|
|
460
|
+
}
|
|
461
|
+
files['tsconfig.base.json'] = toJson({
|
|
462
|
+
$schema: 'https://json.schemastore.org/tsconfig',
|
|
463
|
+
compilerOptions: {
|
|
464
|
+
target: 'ES2022',
|
|
465
|
+
module: 'ESNext',
|
|
466
|
+
moduleResolution: 'Bundler',
|
|
467
|
+
lib: tsconfigLib,
|
|
468
|
+
strict: true,
|
|
469
|
+
esModuleInterop: true,
|
|
470
|
+
skipLibCheck: true,
|
|
471
|
+
declaration: true,
|
|
472
|
+
noEmit: true,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
files['eslint.config.js'] = [
|
|
476
|
+
`import js from '@eslint/js';`,
|
|
477
|
+
`import tseslint from 'typescript-eslint';`,
|
|
478
|
+
``,
|
|
479
|
+
`export default tseslint.config(`,
|
|
480
|
+
`\tjs.configs.recommended,`,
|
|
481
|
+
`\t...tseslint.configs.recommended,`,
|
|
482
|
+
`\t{ ignores: ['**/dist'] },`,
|
|
483
|
+
`);`,
|
|
484
|
+
``,
|
|
485
|
+
].join('\n');
|
|
486
|
+
files['.prettierrc.json'] = toJson({ useTabs: true, singleQuote: true, semi: true, printWidth: 100, trailingComma: 'all' });
|
|
487
|
+
files['.github/workflows/ci.yml'] = ciWorkflow(cfg, cfg.packageManager);
|
|
488
|
+
files['packkit.json'] = provenance(cfg);
|
|
489
|
+
return files;
|
|
490
|
+
}
|
|
491
|
+
|
|
518
492
|
function addPackage(files, { name, dir, src, test, deps }) {
|
|
519
493
|
const pkg = {
|
|
520
494
|
name,
|
package/src/core/options.js
CHANGED
|
@@ -353,5 +353,28 @@ export function normalizeConfig(input = {}, diagnostics = null) {
|
|
|
353
353
|
if (cfg.release !== 'changesets') coerce('canary', false, 'CANARY_REQUIRES_CHANGESETS', 'Canary releases were disabled because they require the Changesets release flow.');
|
|
354
354
|
// JSR is TypeScript-first, ESM, and for plain (non-framework) libraries.
|
|
355
355
|
if (!(cfg.isTs && cfg.hasLibrary && !cfg.hasFramework && !cfg.hasApp)) coerce('jsr', false, 'JSR_REQUIRES_PLAIN_TS_LIBRARY', 'JSR publishing was disabled because it applies only to a plain TypeScript library.');
|
|
356
|
+
|
|
357
|
+
cfg.resolved = resolvedView(cfg);
|
|
356
358
|
return cfg;
|
|
357
359
|
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* A grouped, read-only view of the derived state — the same booleans the flat
|
|
363
|
+
* `cfg.has*`/`is*` flags expose, organized by concern. Consumers that prefer
|
|
364
|
+
* structured access (`cfg.resolved.targets.app`) can use it instead of
|
|
365
|
+
* re-interpreting raw selections; the flat flags remain for existing code.
|
|
366
|
+
*
|
|
367
|
+
* It is derived purely from the flags computed above, and test/invariants.test.js
|
|
368
|
+
* asserts the two never disagree and that the state is internally consistent —
|
|
369
|
+
* so the derived model can't silently contradict itself.
|
|
370
|
+
*/
|
|
371
|
+
function resolvedView(cfg) {
|
|
372
|
+
return {
|
|
373
|
+
targets: { library: cfg.hasLibrary, cli: cfg.hasCli, service: cfg.hasService, app: cfg.hasApp },
|
|
374
|
+
language: { typescript: cfg.isTs, ext: cfg.ext, srcExt: cfg.srcExt },
|
|
375
|
+
framework: { name: cfg.framework, react: cfg.isReact, vue: cfg.isVue, svelte: cfg.isSvelte, any: cfg.hasFramework },
|
|
376
|
+
build: { vite: cfg.viteBuild, custom: cfg.customBuild, usesVite: cfg.usesVite, has: cfg.hasBuild },
|
|
377
|
+
module: { format: cfg.moduleFormat, esm: cfg.hasEsm, cjs: cfg.hasCjs },
|
|
378
|
+
package: { publishable: cfg.publishable, monorepo: cfg.monorepo },
|
|
379
|
+
};
|
|
380
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// The single source of truth for every dependency version Packkit writes into
|
|
2
|
+
// generated projects. Feature modules reference these by name instead of
|
|
3
|
+
// hard-coding a spec, so a bump happens in one place and the freshness workflow
|
|
4
|
+
// (scripts/check-template-deps.mjs) has one authoritative list to check.
|
|
5
|
+
//
|
|
6
|
+
// A conformance test (test/versions.test.js) asserts that every version in the
|
|
7
|
+
// generated output comes from this catalog, so nothing can drift back into a
|
|
8
|
+
// feature module unnoticed.
|
|
9
|
+
//
|
|
10
|
+
// When you bump anything here, update LAST_REVIEWED.
|
|
11
|
+
|
|
12
|
+
export const LAST_REVIEWED = '2026-07-26';
|
|
13
|
+
|
|
14
|
+
// name → npm spec. Deps that ship at one version everywhere.
|
|
15
|
+
export const V = {
|
|
16
|
+
// toolchain
|
|
17
|
+
typescript: '^5.9.3',
|
|
18
|
+
'typescript-eslint': '^8.0.0',
|
|
19
|
+
eslint: '^10.0.0',
|
|
20
|
+
'@eslint/js': '^10.0.0',
|
|
21
|
+
prettier: '^3.3.0',
|
|
22
|
+
'@biomejs/biome': '^2.0.0',
|
|
23
|
+
oxlint: '^1.0.0',
|
|
24
|
+
turbo: '^2.0.0',
|
|
25
|
+
rimraf: '^6.0.0',
|
|
26
|
+
'@types/node': '^24.0.0',
|
|
27
|
+
|
|
28
|
+
// build
|
|
29
|
+
tsup: '^8.0.0',
|
|
30
|
+
tsdown: '^0.6.0',
|
|
31
|
+
unbuild: '^3.0.0',
|
|
32
|
+
rollup: '^4.0.0',
|
|
33
|
+
'@rollup/plugin-typescript': '^12.0.0',
|
|
34
|
+
'@rollup/plugin-terser': '^1.0.0',
|
|
35
|
+
tslib: '^2.6.0',
|
|
36
|
+
tsx: '^4.0.0',
|
|
37
|
+
vite: '^8.0.0',
|
|
38
|
+
'vite-plugin-dts': '^5.0.0',
|
|
39
|
+
|
|
40
|
+
// test
|
|
41
|
+
vitest: '^4.0.0',
|
|
42
|
+
'@vitest/coverage-v8': '^4.0.0',
|
|
43
|
+
jsdom: '^29.0.0',
|
|
44
|
+
jest: '^30.0.0',
|
|
45
|
+
'@types/jest': '^30.0.0',
|
|
46
|
+
'ts-jest': '^29.0.0',
|
|
47
|
+
supertest: '^7.0.0',
|
|
48
|
+
'@types/supertest': '^6.0.0',
|
|
49
|
+
'@playwright/test': '^1.50.0',
|
|
50
|
+
|
|
51
|
+
// release / package checks
|
|
52
|
+
'@changesets/cli': '^2.27.0',
|
|
53
|
+
'release-it': '^21.0.0',
|
|
54
|
+
np: '^12.0.0',
|
|
55
|
+
publint: '^0.3.0',
|
|
56
|
+
'@arethetypeswrong/cli': '^0.18.0',
|
|
57
|
+
knip: '^5.0.0',
|
|
58
|
+
'size-limit': '^11.0.0',
|
|
59
|
+
'@size-limit/preset-small-lib': '^11.0.0',
|
|
60
|
+
|
|
61
|
+
// git hooks
|
|
62
|
+
'simple-git-hooks': '^2.11.0',
|
|
63
|
+
husky: '^9.1.0',
|
|
64
|
+
lefthook: '^2.0.0',
|
|
65
|
+
'lint-staged': '^16.2.0',
|
|
66
|
+
|
|
67
|
+
// react
|
|
68
|
+
react: '^19.0.0',
|
|
69
|
+
'react-dom': '^19.0.0',
|
|
70
|
+
'@types/react': '^19.0.0',
|
|
71
|
+
'@types/react-dom': '^19.0.0',
|
|
72
|
+
'@vitejs/plugin-react': '^6.0.0',
|
|
73
|
+
'@testing-library/react': '^16.0.0',
|
|
74
|
+
'@testing-library/dom': '^10.0.0',
|
|
75
|
+
|
|
76
|
+
// vue
|
|
77
|
+
vue: '^3.4.0',
|
|
78
|
+
'vue-tsc': '^3.0.0',
|
|
79
|
+
'@vitejs/plugin-vue': '^6.0.0',
|
|
80
|
+
'@testing-library/vue': '^8.1.0',
|
|
81
|
+
|
|
82
|
+
// svelte
|
|
83
|
+
svelte: '^5.0.0',
|
|
84
|
+
'svelte-check': '^4.0.0',
|
|
85
|
+
'@sveltejs/vite-plugin-svelte': '^7.0.0',
|
|
86
|
+
'@testing-library/svelte': '^5.2.0',
|
|
87
|
+
|
|
88
|
+
// storybook
|
|
89
|
+
storybook: '^10.0.0',
|
|
90
|
+
'@storybook/react': '^10.0.0',
|
|
91
|
+
'@storybook/react-vite': '^10.0.0',
|
|
92
|
+
'@storybook/vue3': '^10.0.0',
|
|
93
|
+
'@storybook/vue3-vite': '^10.0.0',
|
|
94
|
+
'@storybook/svelte': '^10.0.0',
|
|
95
|
+
'@storybook/svelte-vite': '^10.0.0',
|
|
96
|
+
|
|
97
|
+
// services
|
|
98
|
+
hono: '^4.5.0',
|
|
99
|
+
'@hono/node-server': '^2.0.0',
|
|
100
|
+
fastify: '^5.0.0',
|
|
101
|
+
express: '^5.0.0',
|
|
102
|
+
'@types/express': '^5.0.0',
|
|
103
|
+
zod: '^4.0.0',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Peer-dependency ranges — deliberately looser than the direct-dependency spec,
|
|
107
|
+
// so a component library declares broad compatibility rather than a hard pin.
|
|
108
|
+
export const PEER = {
|
|
109
|
+
react: '>=18',
|
|
110
|
+
'react-dom': '>=18',
|
|
111
|
+
vue: '>=3',
|
|
112
|
+
svelte: '>=5',
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Deps intentionally held below their latest major, with the reason. The
|
|
116
|
+
// freshness check reads this so a held-back dep is reported, not failed.
|
|
117
|
+
export const HELD = {
|
|
118
|
+
typescript: 'typescript-eslint peers typescript <6.1.0 (no TS 7 support yet)',
|
|
119
|
+
knip: 'v6 crashes on the oxc-parser native binding',
|
|
120
|
+
'lint-staged': 'v17 requires Node >=22.22.1, above our Node 22 engines floor; hold at 16 to keep the Maintenance-LTS line working',
|
|
121
|
+
};
|