create-packkit 2.9.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 +36 -2
- package/package.json +23 -4
- 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/index.js +41 -7
- package/src/core/monorepo.js +75 -101
- package/src/core/options.js +67 -22
- package/src/core/render.js +6 -0
- package/src/core/versions.js +121 -0
- package/src/embedded/contract.js +66 -0
- package/src/embedded/index.js +506 -0
- package/src/embedded/paths.js +88 -0
- package/src/embedded/pkg-merge.js +89 -0
- package/src/embedded/upgrade.js +148 -0
- package/src/embedded/writer.js +151 -0
- package/src/index.js +12 -0
- package/types/core.d.ts +96 -0
- package/types/embedded.d.ts +112 -0
- package/types/index.d.ts +6 -0
- package/types/writer.d.ts +25 -0
|
@@ -54,7 +54,8 @@ export default {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
function libraryEntry(cfg) {
|
|
57
|
-
|
|
57
|
+
// Only reached for a plain library (the caller guards on !cfg.hasFramework),
|
|
58
|
+
// so framework entries never come through here — frameworks.js writes those.
|
|
58
59
|
if (cfg.isTs) {
|
|
59
60
|
return [
|
|
60
61
|
`/** Greet someone by name. */`,
|
|
@@ -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: 'release',
|
|
@@ -21,7 +22,7 @@ export default {
|
|
|
21
22
|
pkg.scripts.changeset = 'changeset';
|
|
22
23
|
pkg.scripts.version = 'changeset version';
|
|
23
24
|
pkg.scripts.release = `${buildThen(cfg)}changeset publish`;
|
|
24
|
-
pkg.devDependencies['@changesets/cli'] = '
|
|
25
|
+
pkg.devDependencies['@changesets/cli'] = V['@changesets/cli'];
|
|
25
26
|
} else if (cfg.release === 'release-it') {
|
|
26
27
|
files['.release-it.json'] = toJson({
|
|
27
28
|
git: { commitMessage: 'chore: release v${version}' },
|
|
@@ -29,10 +30,10 @@ export default {
|
|
|
29
30
|
npm: { publish: true },
|
|
30
31
|
});
|
|
31
32
|
pkg.scripts.release = 'release-it';
|
|
32
|
-
pkg.devDependencies['release-it'] = '
|
|
33
|
+
pkg.devDependencies['release-it'] = V['release-it'];
|
|
33
34
|
} else if (cfg.release === 'np') {
|
|
34
35
|
pkg.scripts.release = 'np';
|
|
35
|
-
pkg.devDependencies.np =
|
|
36
|
+
pkg.devDependencies.np = V.np;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
return { files, pkg };
|
|
@@ -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/index.js
CHANGED
|
@@ -22,23 +22,49 @@ export function fromPreset(name, overrides = {}) {
|
|
|
22
22
|
return cfg;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Run every active feature, collecting its files and package.json fragment.
|
|
27
|
+
* Returns the raw material — file map, the fragments in contribution order,
|
|
28
|
+
* and which feature produced each file — so callers that need provenance (the
|
|
29
|
+
* embedded API's collision detection) get it without a second pass, while
|
|
30
|
+
* `generate` folds it into the same output as before.
|
|
31
|
+
*/
|
|
32
|
+
export function assemble(cfg) {
|
|
30
33
|
const files = {};
|
|
34
|
+
const fileSources = {};
|
|
35
|
+
const fragments = [];
|
|
31
36
|
let pkg = {};
|
|
32
37
|
|
|
33
38
|
for (const feat of features) {
|
|
34
39
|
if (!feat.active(cfg)) continue;
|
|
35
40
|
const out = feat.apply(cfg) || {};
|
|
36
41
|
if (out.files) {
|
|
37
|
-
for (const [path, contents] of Object.entries(out.files))
|
|
42
|
+
for (const [path, contents] of Object.entries(out.files)) {
|
|
43
|
+
(fileSources[path] ||= []).push(feat.id);
|
|
44
|
+
files[path] = contents;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (out.pkg) {
|
|
48
|
+
fragments.push({ source: feat.id, pkg: out.pkg });
|
|
49
|
+
pkg = deepMerge(pkg, out.pkg);
|
|
38
50
|
}
|
|
39
|
-
if (out.pkg) pkg = deepMerge(pkg, out.pkg);
|
|
40
51
|
}
|
|
52
|
+
return { files, fileSources, fragments, pkg };
|
|
53
|
+
}
|
|
41
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Generate, keeping the provenance assemble() produced. One assembly pass feeds
|
|
57
|
+
* both the public files and the embedded API's conflict diagnostics, so the
|
|
58
|
+
* bytes callers get and the provenance they inspect come from the same run.
|
|
59
|
+
*/
|
|
60
|
+
export function generateTracked(input, diagnostics = null) {
|
|
61
|
+
const cfg = normalizeConfig(input, diagnostics);
|
|
62
|
+
if (cfg.monorepo) {
|
|
63
|
+
// The monorepo generator is a separate path with no per-feature fragments.
|
|
64
|
+
return { ...buildMonorepo(cfg), fileSources: {}, fragments: [] };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { files, fileSources, fragments, pkg } = assemble(cfg);
|
|
42
68
|
files['package.json'] = toJson(finalizePackageJson(pkg));
|
|
43
69
|
files['packkit.json'] = provenance(cfg);
|
|
44
70
|
|
|
@@ -47,9 +73,17 @@ export function generate(input) {
|
|
|
47
73
|
files,
|
|
48
74
|
postCommands: postCommands(cfg),
|
|
49
75
|
summary: summarize(cfg, files),
|
|
76
|
+
fileSources,
|
|
77
|
+
fragments,
|
|
50
78
|
};
|
|
51
79
|
}
|
|
52
80
|
|
|
81
|
+
/** Turn a config into a complete set of files. */
|
|
82
|
+
export function generate(input) {
|
|
83
|
+
const { config, files, postCommands, summary } = generateTracked(input);
|
|
84
|
+
return { config, files, postCommands, summary };
|
|
85
|
+
}
|
|
86
|
+
|
|
53
87
|
function postCommands(cfg) {
|
|
54
88
|
const install = {
|
|
55
89
|
npm: 'npm install',
|
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,
|