create-packkit 2.3.0 → 2.4.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/package.json +1 -1
- package/src/cli/args.js +1 -0
- package/src/cli/index.js +1 -0
- package/src/cli/wizard.js +3 -0
- package/src/core/features/service.js +78 -11
- package/src/core/features/test.js +24 -3
- package/src/core/options.js +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-packkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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
package/src/cli/index.js
CHANGED
|
@@ -39,6 +39,7 @@ Options:
|
|
|
39
39
|
--pm <manager> npm | pnpm | yarn | bun
|
|
40
40
|
--language <ts|js> --module <esm|cjs|dual> --framework <none|react|vue|svelte>
|
|
41
41
|
--target <library|cli|service|app> (repeatable) --storybook
|
|
42
|
+
--server <hono|fastify|express> (HTTP service framework)
|
|
42
43
|
--bundler <tsup|tsdown|unbuild|rollup|none> --minify
|
|
43
44
|
--test <vitest|jest|node|none> --e2e (Playwright, for apps)
|
|
44
45
|
--env (type-safe env validation, services/CLIs) --no-sourcemaps
|
package/src/cli/wizard.js
CHANGED
|
@@ -36,6 +36,9 @@ export async function runWizard(seed = {}) {
|
|
|
36
36
|
cfg.minify = bail(await p.confirm({ message: 'Minify the build output?', initialValue: false }));
|
|
37
37
|
}
|
|
38
38
|
cfg.test = bail(await p.select({ message: 'Test runner', options: asOptions('test'), initialValue: OPTIONS.test.default }));
|
|
39
|
+
if (cfg.target.includes('service')) {
|
|
40
|
+
cfg.serviceFramework = bail(await p.select({ message: 'Service framework', options: asOptions('serviceFramework'), initialValue: OPTIONS.serviceFramework.default }));
|
|
41
|
+
}
|
|
39
42
|
if (cfg.target.includes('app')) {
|
|
40
43
|
cfg.e2e = bail(await p.confirm({ message: 'Add Playwright end-to-end tests?', initialValue: false }));
|
|
41
44
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
// HTTP service target
|
|
2
|
-
// (which listens), adds start/dev scripts,
|
|
1
|
+
// HTTP service target. Supports Hono (default), Fastify and Express. Splits the
|
|
2
|
+
// app (testable) from the server entry (which listens), adds start/dev scripts,
|
|
3
|
+
// and a production Dockerfile.
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
id: 'service',
|
|
6
7
|
active: (cfg) => cfg.hasService,
|
|
7
8
|
apply(cfg) {
|
|
8
9
|
const ext = cfg.ext;
|
|
10
|
+
const fw = cfg.serviceFramework || 'hono';
|
|
9
11
|
const files = {
|
|
10
|
-
[`src/app.${ext}`]: appFile(cfg),
|
|
11
|
-
[`src/index.${ext}`]: serverFile(cfg),
|
|
12
|
+
[`src/app.${ext}`]: appFile(cfg, fw),
|
|
13
|
+
[`src/index.${ext}`]: serverFile(cfg, fw),
|
|
12
14
|
Dockerfile: dockerfile(cfg),
|
|
13
15
|
'.dockerignore': ['node_modules', 'dist', 'coverage', '.git', '.github', '.env', '.env.*', '!.env.example', '*.log', 'Dockerfile', '.dockerignore', ''].join('\n'),
|
|
14
16
|
};
|
|
@@ -20,15 +22,50 @@ export default {
|
|
|
20
22
|
start: 'node dist/index.js',
|
|
21
23
|
dev: cfg.isTs ? 'tsx watch src/index.ts' : 'node --watch src/index.js',
|
|
22
24
|
},
|
|
23
|
-
dependencies:
|
|
24
|
-
|
|
25
|
+
dependencies: deps(fw),
|
|
26
|
+
devDependencies: {
|
|
27
|
+
...(cfg.isTs ? { tsx: '^4.0.0' } : {}),
|
|
28
|
+
...(cfg.isTs ? typeDeps(fw) : {}),
|
|
29
|
+
},
|
|
25
30
|
},
|
|
26
31
|
};
|
|
27
32
|
},
|
|
28
33
|
};
|
|
29
34
|
|
|
30
|
-
function
|
|
31
|
-
|
|
35
|
+
function deps(fw) {
|
|
36
|
+
if (fw === 'fastify') return { fastify: '^5.0.0' };
|
|
37
|
+
if (fw === 'express') return { express: '^5.0.0' };
|
|
38
|
+
return { hono: '^4.5.0', '@hono/node-server': '^2.0.0' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function typeDeps(fw) {
|
|
42
|
+
if (fw === 'express') return { '@types/express': '^5.0.0' };
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function appFile(cfg, fw) {
|
|
47
|
+
if (fw === 'fastify') {
|
|
48
|
+
return [
|
|
49
|
+
`import Fastify from 'fastify';`,
|
|
50
|
+
``,
|
|
51
|
+
`export const app = Fastify();`,
|
|
52
|
+
``,
|
|
53
|
+
`app.get('/', async () => ({ ok: true, service: '${cfg.name}' }));`,
|
|
54
|
+
`app.get('/health', async () => 'ok');`,
|
|
55
|
+
``,
|
|
56
|
+
].join('\n');
|
|
57
|
+
}
|
|
58
|
+
if (fw === 'express') {
|
|
59
|
+
return [
|
|
60
|
+
`import express from 'express';`,
|
|
61
|
+
``,
|
|
62
|
+
`export const app = express();`,
|
|
63
|
+
``,
|
|
64
|
+
`app.get('/', (_req, res) => res.json({ ok: true, service: '${cfg.name}' }));`,
|
|
65
|
+
`app.get('/health', (_req, res) => res.send('ok'));`,
|
|
66
|
+
``,
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
32
69
|
return [
|
|
33
70
|
`import { Hono } from 'hono';`,
|
|
34
71
|
``,
|
|
@@ -40,13 +77,43 @@ function appFile(cfg) {
|
|
|
40
77
|
].join('\n');
|
|
41
78
|
}
|
|
42
79
|
|
|
43
|
-
function serverFile(cfg) {
|
|
80
|
+
function serverFile(cfg, fw) {
|
|
81
|
+
const port = cfg.env ? 'env.PORT' : 'Number(process.env.PORT) || 3000';
|
|
82
|
+
const envImport = cfg.env ? `import { env } from './env.js';` : null;
|
|
83
|
+
|
|
84
|
+
if (fw === 'fastify') {
|
|
85
|
+
return [
|
|
86
|
+
`import { app } from './app.js';`,
|
|
87
|
+
envImport,
|
|
88
|
+
``,
|
|
89
|
+
`const port = ${port};`,
|
|
90
|
+
`app.listen({ port, host: '0.0.0.0' }).then((url) => {`,
|
|
91
|
+
`\tconsole.log(\`Listening on \${url}\`);`,
|
|
92
|
+
`}).catch((err) => {`,
|
|
93
|
+
`\tapp.log.error(err);`,
|
|
94
|
+
`\tprocess.exit(1);`,
|
|
95
|
+
`});`,
|
|
96
|
+
``,
|
|
97
|
+
].filter((l) => l !== null).join('\n');
|
|
98
|
+
}
|
|
99
|
+
if (fw === 'express') {
|
|
100
|
+
return [
|
|
101
|
+
`import { app } from './app.js';`,
|
|
102
|
+
envImport,
|
|
103
|
+
``,
|
|
104
|
+
`const port = ${port};`,
|
|
105
|
+
`app.listen(port, () => {`,
|
|
106
|
+
`\tconsole.log(\`Listening on http://localhost:\${port}\`);`,
|
|
107
|
+
`});`,
|
|
108
|
+
``,
|
|
109
|
+
].filter((l) => l !== null).join('\n');
|
|
110
|
+
}
|
|
44
111
|
return [
|
|
45
112
|
`import { serve } from '@hono/node-server';`,
|
|
46
113
|
`import { app } from './app.js';`,
|
|
47
|
-
|
|
114
|
+
envImport,
|
|
48
115
|
``,
|
|
49
|
-
`const port = ${
|
|
116
|
+
`const port = ${port};`,
|
|
50
117
|
`serve({ fetch: app.fetch, port }, (info) => {`,
|
|
51
118
|
`\tconsole.log(\`Listening on http://localhost:\${info.port}\`);`,
|
|
52
119
|
`});`,
|
|
@@ -68,6 +68,12 @@ export default {
|
|
|
68
68
|
files[`src/index.test.${testExt}`] = exampleTest('node', cfg);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Express services test through supertest (no built-in inject).
|
|
72
|
+
if (cfg.hasService && cfg.serviceFramework === 'express') {
|
|
73
|
+
pkg.devDependencies.supertest = '^7.0.0';
|
|
74
|
+
if (cfg.isTs) pkg.devDependencies['@types/supertest'] = '^6.0.0';
|
|
75
|
+
}
|
|
76
|
+
|
|
71
77
|
return { files, pkg };
|
|
72
78
|
},
|
|
73
79
|
};
|
|
@@ -82,13 +88,28 @@ function exampleTest(runner, cfg) {
|
|
|
82
88
|
const imp = importPath(runner, cfg);
|
|
83
89
|
if (cfg.hasService) {
|
|
84
90
|
const api = runner === 'jest' ? '' : `import { describe, it, expect } from 'vitest';\n`;
|
|
91
|
+
const fw = cfg.serviceFramework || 'hono';
|
|
92
|
+
let imports, call, statusProp;
|
|
93
|
+
if (fw === 'fastify') {
|
|
94
|
+
imports = `import { app } from './app.js';`;
|
|
95
|
+
call = `await app.inject({ method: 'GET', url: '/' })`;
|
|
96
|
+
statusProp = 'statusCode';
|
|
97
|
+
} else if (fw === 'express') {
|
|
98
|
+
imports = `import request from 'supertest';\nimport { app } from './app.js';`;
|
|
99
|
+
call = `await request(app).get('/')`;
|
|
100
|
+
statusProp = 'status';
|
|
101
|
+
} else {
|
|
102
|
+
imports = `import { app } from './app.js';`;
|
|
103
|
+
call = `await app.request('/')`;
|
|
104
|
+
statusProp = 'status';
|
|
105
|
+
}
|
|
85
106
|
return [
|
|
86
|
-
api +
|
|
107
|
+
api + imports,
|
|
87
108
|
``,
|
|
88
109
|
`describe('app', () => {`,
|
|
89
110
|
`\tit('responds on /', async () => {`,
|
|
90
|
-
`\t\tconst res =
|
|
91
|
-
`\t\texpect(res
|
|
111
|
+
`\t\tconst res = ${call};`,
|
|
112
|
+
`\t\texpect(res.${statusProp}).toBe(200);`,
|
|
92
113
|
`\t});`,
|
|
93
114
|
`});`,
|
|
94
115
|
``,
|
package/src/core/options.js
CHANGED
|
@@ -34,6 +34,14 @@ export const OPTIONS = {
|
|
|
34
34
|
{ value: 'cjs', label: 'CommonJS only' },
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
|
+
serviceFramework: {
|
|
38
|
+
group: 'core', type: 'select', label: 'Service framework (HTTP service)', default: 'hono',
|
|
39
|
+
choices: [
|
|
40
|
+
{ value: 'hono', label: 'Hono (fast, web-standard)' },
|
|
41
|
+
{ value: 'fastify', label: 'Fastify' },
|
|
42
|
+
{ value: 'express', label: 'Express' },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
37
45
|
target: {
|
|
38
46
|
group: 'core', type: 'multiselect', label: 'What are you building?', default: ['library'],
|
|
39
47
|
choices: [
|