create-packkit 2.0.3 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-packkit",
3
- "version": "2.0.3",
3
+ "version": "2.1.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
@@ -48,6 +48,7 @@ export function parseCliArgs(argv) {
48
48
  minify: { type: 'boolean' },
49
49
  monorepo: { type: 'boolean' },
50
50
  storybook: { type: 'boolean' },
51
+ e2e: { type: 'boolean' },
51
52
  'pkg-checks': { type: 'boolean' },
52
53
  knip: { type: 'boolean' },
53
54
  jsr: { type: 'boolean' },
@@ -74,6 +75,7 @@ export function parseCliArgs(argv) {
74
75
  if (values.minify) overrides.minify = true;
75
76
  if (values.monorepo) overrides.monorepo = true;
76
77
  if (values.storybook) overrides.storybook = true;
78
+ if (values.e2e) overrides.e2e = true;
77
79
  if (values['pkg-checks']) overrides.pkgChecks = true;
78
80
  if (values.knip) overrides.knip = true;
79
81
  if (values.jsr) overrides.jsr = true;
package/src/cli/index.js CHANGED
@@ -40,7 +40,8 @@ Options:
40
40
  --language <ts|js> --module <esm|cjs|dual> --framework <none|react|vue|svelte>
41
41
  --target <library|cli|service|app> (repeatable) --storybook
42
42
  --bundler <tsup|tsdown|unbuild|rollup|none> --minify
43
- --test <vitest|jest|node|none> --lint <eslint-prettier|biome|oxlint|none>
43
+ --test <vitest|jest|node|none> --e2e (Playwright, for apps)
44
+ --lint <eslint-prettier|biome|oxlint|none>
44
45
  --hooks <simple-git-hooks|husky|lefthook|none> --release <changesets|release-it|np|none>
45
46
  --workflows <ci|npm-publish|pages|codeql|codecov|stale> (repeatable)
46
47
  --deps <renovate|dependabot|none> --license <MIT|Apache-2.0|ISC|none>
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('app')) {
40
+ cfg.e2e = bail(await p.confirm({ message: 'Add Playwright end-to-end tests?', initialValue: false }));
41
+ }
39
42
  cfg.lint = bail(await p.select({ message: 'Lint / format', options: asOptions('lint'), initialValue: OPTIONS.lint.default }));
40
43
  cfg.gitHooks = bail(await p.select({ message: 'Git hooks', options: asOptions('gitHooks'), initialValue: OPTIONS.gitHooks.default }));
41
44
  cfg.release = bail(await p.select({ message: 'Release / versioning', options: asOptions('release'), initialValue: OPTIONS.release.default }));
@@ -0,0 +1,47 @@
1
+ // Playwright end-to-end tests for app targets: a config that boots the Vite
2
+ // dev server, one smoke spec, the scripts and the dev dependency. The CI job
3
+ // lives in workflows.js, which owns all workflow YAML.
4
+
5
+ const DEV_URL = 'http://localhost:5173'; // Vite's default dev port
6
+
7
+ const runDev = (cfg) => (cfg.packageManager === 'npm' ? 'npm run dev' : `${cfg.packageManager} dev`);
8
+
9
+ export default {
10
+ id: 'e2e',
11
+ active: (cfg) => cfg.e2e && cfg.hasApp,
12
+ apply(cfg) {
13
+ const ext = cfg.ext; // 'ts' | 'js' — config/specs never need JSX
14
+ const files = {};
15
+ const pkg = { scripts: {}, devDependencies: { '@playwright/test': '^1.50.0' } };
16
+
17
+ files[`playwright.config.${ext}`] = [
18
+ `import { defineConfig, devices } from '@playwright/test';`,
19
+ ``,
20
+ `export default defineConfig({`,
21
+ `\ttestDir: './e2e',`,
22
+ `\tuse: { baseURL: '${DEV_URL}', trace: 'on-first-retry' },`,
23
+ `\t// Playwright starts the app for you and waits for it to be ready.`,
24
+ `\twebServer: {`,
25
+ `\t\tcommand: '${runDev(cfg)}',`,
26
+ `\t\turl: '${DEV_URL}',`,
27
+ `\t\treuseExistingServer: !process.env.CI,`,
28
+ `\t},`,
29
+ `\tprojects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],`,
30
+ `});`,
31
+ ``,
32
+ ].join('\n');
33
+
34
+ files[`e2e/app.spec.${ext}`] = [
35
+ `import { test, expect } from '@playwright/test';`,
36
+ ``,
37
+ `test('renders the app', async ({ page }) => {`,
38
+ `\tawait page.goto('/');`,
39
+ `\tawait expect(page.getByRole('heading', { name: /Hello from/ })).toBeVisible();`,
40
+ `});`,
41
+ ``,
42
+ ].join('\n');
43
+
44
+ pkg.scripts['test:e2e'] = 'playwright test';
45
+ return { files, pkg };
46
+ },
47
+ };
@@ -10,6 +10,11 @@ dist/
10
10
  coverage/
11
11
  *.tsbuildinfo
12
12
 
13
+ # test artifacts (Playwright)
14
+ test-results/
15
+ playwright-report/
16
+ playwright/.cache/
17
+
13
18
  # logs
14
19
  *.log
15
20
  npm-debug.log*
@@ -8,6 +8,7 @@ import frameworks from './frameworks.js';
8
8
  import vite from './vite.js';
9
9
  import service from './service.js';
10
10
  import test from './test.js';
11
+ import e2e from './e2e.js';
11
12
  import lint from './lint.js';
12
13
  import githooks from './githooks.js';
13
14
  import release from './release.js';
@@ -29,6 +30,7 @@ export default [
29
30
  vite,
30
31
  service,
31
32
  test,
33
+ e2e,
32
34
  lint,
33
35
  githooks,
34
36
  release,
@@ -26,6 +26,9 @@ export default {
26
26
  `export default defineConfig({`,
27
27
  fw ? `\tplugins: [${fw.call}],` : null,
28
28
  `\ttest: {`,
29
+ // Keep Vitest to unit tests under src/ so it never tries to run the
30
+ // Playwright specs in e2e/ (they share the *.spec.ts glob).
31
+ cfg.e2e ? `\t\tinclude: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}'],` : null,
29
32
  cfg.hasFramework ? `\t\tenvironment: 'jsdom',` : null,
30
33
  cfg.hasFramework ? `\t\tglobals: true,` : null,
31
34
  cfg.coverage ? `\t\tcoverage: { provider: 'v8', reporter: ['text', 'lcov'] },` : null,
@@ -41,6 +41,7 @@ export default {
41
41
  if (wf.includes('pages')) files['.github/workflows/pages.yml'] = pagesWorkflow(cfg);
42
42
  if (wf.includes('codeql')) files['.github/workflows/codeql.yml'] = codeqlWorkflow();
43
43
  if (wf.includes('stale')) files['.github/workflows/stale.yml'] = staleWorkflow();
44
+ if (cfg.e2e && cfg.hasApp && wf.includes('ci')) files['.github/workflows/e2e.yml'] = e2eWorkflow(cfg);
44
45
 
45
46
  if (cfg.deps === 'renovate') {
46
47
  files['.github/renovate.json'] = toJson({
@@ -199,6 +200,30 @@ function codeqlWorkflow() {
199
200
  ].join('\n');
200
201
  }
201
202
 
203
+ function e2eWorkflow(cfg) {
204
+ return [
205
+ 'name: E2E',
206
+ 'on:',
207
+ ' push:',
208
+ ' branches: [main]',
209
+ ' pull_request:',
210
+ 'jobs:',
211
+ ' e2e:',
212
+ ' runs-on: ubuntu-latest',
213
+ ' steps:',
214
+ setupSteps(cfg),
215
+ ' - run: npx playwright install --with-deps chromium',
216
+ ` - run: ${pmRun(cfg, 'test:e2e')}`,
217
+ ' - uses: actions/upload-artifact@v4',
218
+ ' if: ${{ !cancelled() }}',
219
+ ' with:',
220
+ ' name: playwright-report',
221
+ ' path: playwright-report/',
222
+ ' retention-days: 7',
223
+ '',
224
+ ].join('\n');
225
+ }
226
+
202
227
  function staleWorkflow() {
203
228
  return [
204
229
  'name: Stale',
@@ -96,6 +96,7 @@ export const OPTIONS = {
96
96
  },
97
97
  coverage: { group: 'quality', type: 'boolean', label: 'Coverage reporting', default: true },
98
98
  storybook: { group: 'quality', type: 'boolean', label: 'Storybook (component libraries)', default: false },
99
+ e2e: { group: 'quality', type: 'boolean', label: 'Playwright end-to-end tests (apps)', default: false },
99
100
  pkgChecks: { group: 'quality', type: 'boolean', label: 'Package checks (publint + are-the-types-wrong)', default: false },
100
101
  knip: { group: 'quality', type: 'boolean', label: 'Knip (unused files / deps / exports)', default: false },
101
102
 
@@ -245,6 +246,9 @@ export function normalizeConfig(input = {}) {
245
246
  // Storybook only applies to component libraries.
246
247
  if (!cfg.hasFramework || cfg.hasApp || !cfg.hasLibrary) cfg.storybook = false;
247
248
 
249
+ // Playwright E2E only applies to app targets.
250
+ if (!cfg.hasApp) cfg.e2e = false;
251
+
248
252
  // A monorepo is its own generation path (see buildMonorepo); it has a build.
249
253
  if (cfg.monorepo) cfg.hasBuild = true;
250
254