create-packkit 1.1.0 → 1.3.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 +8 -1
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/src/core/index.js +3 -0
- package/src/core/monorepo.js +236 -0
- package/src/core/options.js +6 -0
- package/src/core/presets.js +2 -0
package/README.md
CHANGED
|
@@ -45,11 +45,12 @@ No install needed: **[danmat.github.io/create-packkit](https://danmat.github.io/
|
|
|
45
45
|
| **GitHub Actions** | CI · npm publish (provenance) · Pages · CodeQL · Codecov · stale bot |
|
|
46
46
|
| **Deps** | Renovate · Dependabot · none |
|
|
47
47
|
| **Repo** | LICENSE · community files · **AGENTS.md + CLAUDE.md** · VS Code · `.editorconfig` |
|
|
48
|
+
| **Monorepo** | optional pnpm/npm/yarn workspace with **Turborepo** + Changesets + example packages |
|
|
48
49
|
| **Package manager** | npm · pnpm · yarn · bun |
|
|
49
50
|
|
|
50
51
|
## Presets
|
|
51
52
|
|
|
52
|
-
`ts-lib` · `js-lib` · `ts-cli` / `cli` · `react-lib` · `react-lib-js` · `react-app` · `vue-lib` · `vue-app` · `svelte-lib` · `svelte-app` · `node-service` · `oss` · `minimal` · `full` — named bundles of the options above. See the [roadmap](ROADMAP.md) for what's next.
|
|
53
|
+
`ts-lib` · `js-lib` · `ts-cli` / `cli` · `react-lib` · `react-lib-js` · `react-app` · `vue-lib` · `vue-app` · `svelte-lib` · `svelte-app` · `node-service` · `monorepo` · `oss` · `minimal` · `full` — named bundles of the options above. See the [roadmap](ROADMAP.md) for what's next.
|
|
53
54
|
|
|
54
55
|
**Team profiles:** save a partial config as `packkit.config.json` (or any file) and reuse it with `npx create-packkit my-lib --from ./packkit.config.json` — flags still override the file.
|
|
55
56
|
|
|
@@ -64,6 +65,12 @@ npx create-packkit my-lib ts-lib --no-install --no-git # deterministic scaffol
|
|
|
64
65
|
|
|
65
66
|
There's also an [`llms.txt`](llms.txt) (served at [danmat.github.io/create-packkit/llms.txt](https://danmat.github.io/create-packkit/llms.txt)) describing the commands for LLMs.
|
|
66
67
|
|
|
68
|
+
**MCP server** — [`packkit-mcp`](mcp) exposes Packkit as a native [Model Context Protocol](https://modelcontextprotocol.io) tool (schema / preview / scaffold). Add to your agent's MCP config:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{ "mcpServers": { "packkit": { "command": "npx", "args": ["-y", "packkit-mcp"] } } }
|
|
72
|
+
```
|
|
73
|
+
|
|
67
74
|
## How it works
|
|
68
75
|
|
|
69
76
|
Packkit is a pure `config → { files }` **core** that runs in both Node and the browser:
|
package/llms.txt
CHANGED
|
@@ -37,6 +37,10 @@ npx create-packkit lib my-lib --no-install --no-git
|
|
|
37
37
|
|
|
38
38
|
`--language ts|js` · `--framework none|react|vue|svelte` · `--target library|cli|service|app` (repeatable) · `--module esm|cjs|dual` · `--bundler tsup|tsdown|unbuild|rollup|none` · `--minify` · `--test vitest|jest|node|none` · `--lint eslint-prettier|biome|oxlint|none` · `--hooks ...` · `--release changesets|release-it|np|none` · `--workflows ci|npm-publish|pages|codeql|codecov|stale` (repeatable) · `--pkg-checks` (publint + are-the-types-wrong) · `--knip` · `--jsr` · `--deps renovate|dependabot|none` · `--license MIT|Apache-2.0|ISC|none` · `--pm npm|pnpm|yarn|bun` · `--from <file>` (load a JSON profile) · `--no-install` · `--no-git`
|
|
39
39
|
|
|
40
|
+
## MCP server
|
|
41
|
+
|
|
42
|
+
`packkit-mcp` exposes Packkit as Model Context Protocol tools (`packkit_schema`, `packkit_preview`, `packkit_scaffold`). MCP config: `{ "mcpServers": { "packkit": { "command": "npx", "args": ["-y", "packkit-mcp"] } } }`.
|
|
43
|
+
|
|
40
44
|
## Links
|
|
41
45
|
- Web configurator: https://danmat.github.io/create-packkit/
|
|
42
46
|
- Repo: https://github.com/DanMat/create-packkit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-packkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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/core/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { normalizeConfig, OPTIONS, GROUPS, defaultConfig } from './options.js';
|
|
|
6
6
|
import { deepMerge, toJson } from './render.js';
|
|
7
7
|
import { finalizePackageJson } from './pkg.js';
|
|
8
8
|
import features from './features/index.js';
|
|
9
|
+
import { buildMonorepo } from './monorepo.js';
|
|
9
10
|
import { PRESETS, PRESET_NAMES, PRESET_INFO, PRESET_ALIASES, resolvePreset } from './presets.js';
|
|
10
11
|
|
|
11
12
|
export { OPTIONS, GROUPS, defaultConfig, normalizeConfig, PRESETS, PRESET_NAMES, PRESET_INFO, PRESET_ALIASES, resolvePreset };
|
|
@@ -20,6 +21,8 @@ export function fromPreset(name, overrides = {}) {
|
|
|
20
21
|
/** Turn a config into a complete set of files. */
|
|
21
22
|
export function generate(input) {
|
|
22
23
|
const cfg = normalizeConfig(input);
|
|
24
|
+
if (cfg.monorepo) return buildMonorepo(cfg);
|
|
25
|
+
|
|
23
26
|
const files = {};
|
|
24
27
|
let pkg = {};
|
|
25
28
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Monorepo generation — a pnpm/Turborepo (or npm/yarn workspaces) workspace
|
|
2
|
+
// with Changesets and two example packages (one depending on the other).
|
|
3
|
+
// This is a distinct shape from a single package, so generate() delegates here.
|
|
4
|
+
|
|
5
|
+
import { toJson } from './render.js';
|
|
6
|
+
import community from './features/community.js';
|
|
7
|
+
import agents from './features/agents.js';
|
|
8
|
+
import gitfiles from './features/gitfiles.js';
|
|
9
|
+
|
|
10
|
+
export function buildMonorepo(cfg) {
|
|
11
|
+
const files = {};
|
|
12
|
+
const pm = cfg.packageManager;
|
|
13
|
+
const scope = cfg.name.replace(/^@/, '').split('/')[0];
|
|
14
|
+
const core = `@${scope}/core`;
|
|
15
|
+
const utils = `@${scope}/utils`;
|
|
16
|
+
const wsProto = pm === 'pnpm' ? 'workspace:*' : '*';
|
|
17
|
+
|
|
18
|
+
// Reuse the package-agnostic root files (LICENSE, community, AGENTS.md, gitignore).
|
|
19
|
+
for (const feat of [community, agents, gitfiles]) {
|
|
20
|
+
if (feat.active(cfg)) Object.assign(files, feat.apply(cfg).files);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---- root ----
|
|
24
|
+
const rootPkg = {
|
|
25
|
+
name: cfg.name,
|
|
26
|
+
version: '0.0.0',
|
|
27
|
+
private: true,
|
|
28
|
+
type: 'module',
|
|
29
|
+
...(cfg.license !== 'none' ? { license: cfg.license } : {}),
|
|
30
|
+
...(pm === 'pnpm' ? { packageManager: 'pnpm@9.10.0' } : { workspaces: ['packages/*'] }),
|
|
31
|
+
scripts: {
|
|
32
|
+
build: 'turbo build',
|
|
33
|
+
test: 'turbo test',
|
|
34
|
+
lint: 'turbo lint',
|
|
35
|
+
typecheck: 'turbo typecheck',
|
|
36
|
+
dev: 'turbo dev',
|
|
37
|
+
changeset: 'changeset',
|
|
38
|
+
version: 'changeset version',
|
|
39
|
+
release: 'turbo build && changeset publish',
|
|
40
|
+
},
|
|
41
|
+
devDependencies: {
|
|
42
|
+
turbo: '^2.0.0',
|
|
43
|
+
typescript: '^5.5.0',
|
|
44
|
+
tsup: '^8.0.0',
|
|
45
|
+
vitest: '^2.0.0',
|
|
46
|
+
eslint: '^9.0.0',
|
|
47
|
+
'@eslint/js': '^9.0.0',
|
|
48
|
+
'typescript-eslint': '^8.0.0',
|
|
49
|
+
prettier: '^3.3.0',
|
|
50
|
+
'@changesets/cli': '^2.27.0',
|
|
51
|
+
'@types/node': `^${cfg.nodeVersion}.0.0`,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
files['package.json'] = toJson(rootPkg);
|
|
55
|
+
|
|
56
|
+
if (pm === 'pnpm') files['pnpm-workspace.yaml'] = 'packages:\n - "packages/*"\n';
|
|
57
|
+
|
|
58
|
+
files['turbo.json'] = toJson({
|
|
59
|
+
$schema: 'https://turbo.build/schema.json',
|
|
60
|
+
tasks: {
|
|
61
|
+
build: { dependsOn: ['^build'], outputs: ['dist/**'] },
|
|
62
|
+
test: { dependsOn: ['^build'] },
|
|
63
|
+
typecheck: { dependsOn: ['^build'] },
|
|
64
|
+
lint: {},
|
|
65
|
+
dev: { cache: false, persistent: true },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
files['tsconfig.base.json'] = toJson({
|
|
70
|
+
$schema: 'https://json.schemastore.org/tsconfig',
|
|
71
|
+
compilerOptions: {
|
|
72
|
+
target: 'ES2022',
|
|
73
|
+
module: 'ESNext',
|
|
74
|
+
moduleResolution: 'Bundler',
|
|
75
|
+
lib: ['ES2022'],
|
|
76
|
+
strict: true,
|
|
77
|
+
esModuleInterop: true,
|
|
78
|
+
skipLibCheck: true,
|
|
79
|
+
declaration: true,
|
|
80
|
+
noEmit: true,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
files['.changeset/config.json'] = toJson({
|
|
85
|
+
$schema: 'https://unpkg.com/@changesets/config@3.0.0/schema.json',
|
|
86
|
+
changelog: '@changesets/cli/changelog',
|
|
87
|
+
commit: false,
|
|
88
|
+
access: 'public',
|
|
89
|
+
baseBranch: 'main',
|
|
90
|
+
});
|
|
91
|
+
files['.changeset/README.md'] = '# Changesets\n\nRun `npx changeset` to record a version bump for your next release.\n';
|
|
92
|
+
|
|
93
|
+
files['eslint.config.js'] = [
|
|
94
|
+
`import js from '@eslint/js';`,
|
|
95
|
+
`import tseslint from 'typescript-eslint';`,
|
|
96
|
+
``,
|
|
97
|
+
`export default tseslint.config(`,
|
|
98
|
+
`\tjs.configs.recommended,`,
|
|
99
|
+
`\t...tseslint.configs.recommended,`,
|
|
100
|
+
`\t{ ignores: ['**/dist'] },`,
|
|
101
|
+
`);`,
|
|
102
|
+
``,
|
|
103
|
+
].join('\n');
|
|
104
|
+
files['.prettierrc.json'] = toJson({ useTabs: true, singleQuote: true, semi: true, printWidth: 100, trailingComma: 'all' });
|
|
105
|
+
|
|
106
|
+
files['README.md'] = rootReadme(cfg, pm, core, utils);
|
|
107
|
+
files['.github/workflows/ci.yml'] = ciWorkflow(cfg, pm);
|
|
108
|
+
|
|
109
|
+
// ---- packages ----
|
|
110
|
+
addPackage(files, {
|
|
111
|
+
name: core,
|
|
112
|
+
dir: 'packages/core',
|
|
113
|
+
src: [
|
|
114
|
+
`/** Greet someone by name. */`,
|
|
115
|
+
`export function greet(name: string): string {`,
|
|
116
|
+
`\treturn \`Hello, \${name}!\`;`,
|
|
117
|
+
`}`,
|
|
118
|
+
``,
|
|
119
|
+
].join('\n'),
|
|
120
|
+
test: exampleTest(`import { greet } from './index.js';`, `expect(greet('world')).toBe('Hello, world!')`),
|
|
121
|
+
deps: {},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
addPackage(files, {
|
|
125
|
+
name: utils,
|
|
126
|
+
dir: 'packages/utils',
|
|
127
|
+
src: [
|
|
128
|
+
`import { greet } from '${core}';`,
|
|
129
|
+
``,
|
|
130
|
+
`/** Greet someone, loudly. */`,
|
|
131
|
+
`export function shout(name: string): string {`,
|
|
132
|
+
`\treturn greet(name).toUpperCase();`,
|
|
133
|
+
`}`,
|
|
134
|
+
``,
|
|
135
|
+
].join('\n'),
|
|
136
|
+
test: exampleTest(`import { shout } from './index.js';`, `expect(shout('world')).toBe('HELLO, WORLD!')`),
|
|
137
|
+
deps: { [core]: wsProto },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const install = pm === 'npm' ? 'npm install' : `${pm} install`;
|
|
141
|
+
return {
|
|
142
|
+
config: cfg,
|
|
143
|
+
files,
|
|
144
|
+
postCommands: cfg.gitInit ? ['git init', 'git add -A', 'git commit -m "Initial commit from Packkit"'] : [],
|
|
145
|
+
summary: {
|
|
146
|
+
name: cfg.name,
|
|
147
|
+
fileCount: Object.keys(files).length,
|
|
148
|
+
stack: ['monorepo', `${pm}+turbo`, 'TypeScript', 'tsup', 'vitest', 'changesets'],
|
|
149
|
+
workflows: ['ci'],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function addPackage(files, { name, dir, src, test, deps }) {
|
|
155
|
+
const pkg = {
|
|
156
|
+
name,
|
|
157
|
+
version: '0.0.0',
|
|
158
|
+
type: 'module',
|
|
159
|
+
main: './dist/index.js',
|
|
160
|
+
types: './dist/index.d.ts',
|
|
161
|
+
exports: { '.': { types: './dist/index.d.ts', default: './dist/index.js' } },
|
|
162
|
+
files: ['dist'],
|
|
163
|
+
scripts: {
|
|
164
|
+
build: 'tsup src/index.ts --format esm --dts --clean',
|
|
165
|
+
dev: 'tsup src/index.ts --format esm --dts --watch',
|
|
166
|
+
test: 'vitest run',
|
|
167
|
+
typecheck: 'tsc --noEmit',
|
|
168
|
+
lint: 'eslint .',
|
|
169
|
+
},
|
|
170
|
+
...(Object.keys(deps).length ? { dependencies: deps } : {}),
|
|
171
|
+
};
|
|
172
|
+
files[`${dir}/package.json`] = toJson(pkg);
|
|
173
|
+
files[`${dir}/tsconfig.json`] = toJson({ extends: '../../tsconfig.base.json', include: ['src'] });
|
|
174
|
+
files[`${dir}/src/index.ts`] = src;
|
|
175
|
+
files[`${dir}/src/index.test.ts`] = test;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function exampleTest(importLine, assertion) {
|
|
179
|
+
return [`import { describe, it, expect } from 'vitest';`, importLine, ``, `describe('example', () => {`, `\tit('works', () => {`, `\t\t${assertion};`, `\t});`, `});`, ``].join('\n');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function ciWorkflow(cfg, pm) {
|
|
183
|
+
const setup = [' - uses: actions/checkout@v4'];
|
|
184
|
+
if (pm === 'pnpm') setup.push(' - uses: pnpm/action-setup@v4');
|
|
185
|
+
setup.push(
|
|
186
|
+
' - uses: actions/setup-node@v4',
|
|
187
|
+
' with:',
|
|
188
|
+
` node-version: '${cfg.nodeVersion}'`,
|
|
189
|
+
` cache: '${pm === 'yarn' ? 'yarn' : pm === 'pnpm' ? 'pnpm' : 'npm'}'`,
|
|
190
|
+
);
|
|
191
|
+
const install = pm === 'npm' ? 'npm ci' : pm === 'pnpm' ? 'pnpm install --frozen-lockfile' : `${pm} install --frozen-lockfile`;
|
|
192
|
+
const run = (s) => (pm === 'npm' ? `npm run ${s}` : `${pm} ${s}`);
|
|
193
|
+
return [
|
|
194
|
+
'name: CI',
|
|
195
|
+
'on:',
|
|
196
|
+
' push:',
|
|
197
|
+
' branches: [main]',
|
|
198
|
+
' pull_request:',
|
|
199
|
+
'jobs:',
|
|
200
|
+
' ci:',
|
|
201
|
+
' runs-on: ubuntu-latest',
|
|
202
|
+
' steps:',
|
|
203
|
+
setup.join('\n'),
|
|
204
|
+
` - run: ${install}`,
|
|
205
|
+
` - run: ${run('typecheck')}`,
|
|
206
|
+
` - run: ${run('lint')}`,
|
|
207
|
+
` - run: ${run('test')}`,
|
|
208
|
+
` - run: ${run('build')}`,
|
|
209
|
+
'',
|
|
210
|
+
].join('\n');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function rootReadme(cfg, pm, core, utils) {
|
|
214
|
+
const install = pm === 'npm' ? 'npm install' : `${pm} install`;
|
|
215
|
+
const run = (s) => (pm === 'npm' ? `npm run ${s}` : `${pm} ${s}`);
|
|
216
|
+
return [
|
|
217
|
+
`# ${cfg.name}`,
|
|
218
|
+
'',
|
|
219
|
+
cfg.description || '_A monorepo scaffolded with [Packkit](https://danmat.github.io/create-packkit/)._',
|
|
220
|
+
'',
|
|
221
|
+
'## Packages',
|
|
222
|
+
'',
|
|
223
|
+
`- \`${core}\` — the core library`,
|
|
224
|
+
`- \`${utils}\` — utilities built on \`${core}\``,
|
|
225
|
+
'',
|
|
226
|
+
'## Develop',
|
|
227
|
+
'',
|
|
228
|
+
'```sh',
|
|
229
|
+
install,
|
|
230
|
+
run('build') + ' # build all packages (Turborepo)',
|
|
231
|
+
run('test'),
|
|
232
|
+
'```',
|
|
233
|
+
'',
|
|
234
|
+
cfg.license !== 'none' ? `## License\n\n${cfg.license}${cfg.author ? ' © ' + cfg.author : ''}\n` : '',
|
|
235
|
+
].join('\n');
|
|
236
|
+
}
|
package/src/core/options.js
CHANGED
|
@@ -37,6 +37,9 @@ export const OPTIONS = {
|
|
|
37
37
|
{ value: 'app', label: 'App (Vite SPA)' },
|
|
38
38
|
],
|
|
39
39
|
},
|
|
40
|
+
monorepo: {
|
|
41
|
+
group: 'core', type: 'boolean', label: 'Monorepo (pnpm/Turborepo workspace)', default: false,
|
|
42
|
+
},
|
|
40
43
|
framework: {
|
|
41
44
|
group: 'core', type: 'select', label: 'Framework', default: 'none',
|
|
42
45
|
choices: [
|
|
@@ -240,6 +243,9 @@ export function normalizeConfig(input = {}) {
|
|
|
240
243
|
// Storybook only applies to component libraries.
|
|
241
244
|
if (!cfg.hasFramework || cfg.hasApp || !cfg.hasLibrary) cfg.storybook = false;
|
|
242
245
|
|
|
246
|
+
// A monorepo is its own generation path (see buildMonorepo); it has a build.
|
|
247
|
+
if (cfg.monorepo) cfg.hasBuild = true;
|
|
248
|
+
|
|
243
249
|
cfg.publishable = (cfg.hasLibrary || cfg.hasCli) && !cfg.hasApp && !cfg.hasService;
|
|
244
250
|
// Package-correctness checks only make sense for a publishable package.
|
|
245
251
|
if (!cfg.publishable) cfg.pkgChecks = false;
|
package/src/core/presets.js
CHANGED
|
@@ -18,6 +18,7 @@ export const PRESETS = {
|
|
|
18
18
|
test: 'vitest', lint: 'eslint-prettier', gitHooks: 'simple-git-hooks',
|
|
19
19
|
release: 'none', workflows: ['ci'], deps: 'renovate', agents: true, vscode: true,
|
|
20
20
|
},
|
|
21
|
+
monorepo: { monorepo: true, language: 'ts', packageManager: 'pnpm' },
|
|
21
22
|
oss: {
|
|
22
23
|
language: 'ts', target: ['library'], moduleFormat: 'dual', bundler: 'tsup',
|
|
23
24
|
test: 'vitest', coverage: true, lint: 'eslint-prettier', gitHooks: 'simple-git-hooks',
|
|
@@ -76,6 +77,7 @@ export const PRESET_INFO = {
|
|
|
76
77
|
'svelte-lib': 'Svelte component library — ships source, peer svelte, jsdom tests.',
|
|
77
78
|
'svelte-app': 'Svelte SPA — Vite dev server, build, Testing Library.',
|
|
78
79
|
'node-service': 'Node HTTP service (Hono) — tsx dev, tsup build, Dockerfile.',
|
|
80
|
+
monorepo: 'pnpm + Turborepo workspace — two example packages, Changesets, CI.',
|
|
79
81
|
oss: 'Full open-source library — coverage, CodeQL, Codecov, Renovate, Changesets.',
|
|
80
82
|
minimal: 'Bare TS library — tsup only, no tests/lint/CI.',
|
|
81
83
|
full: 'Everything on — library + CLI, all workflows and extras.',
|