create-blitzpack 0.1.14 → 0.1.16
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 +10 -1
- package/dist/index.js +443 -26
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -24,7 +24,16 @@ pnpm create blitzpack [project-name] [options]
|
|
|
24
24
|
- **Web**: Next.js 16 + React 19 + Tailwind CSS v4 + shadcn/ui
|
|
25
25
|
- **API**: Fastify 5 + Prisma 7 + PostgreSQL + Better Auth
|
|
26
26
|
- **Monorepo**: Turborepo + pnpm workspaces
|
|
27
|
-
- **Production-ready**: Auth, admin dashboard, logging, validation, testing
|
|
27
|
+
- **Production-ready app stack**: Auth, admin dashboard, logging, validation, testing
|
|
28
|
+
- **Optional deployment assets**: Dockerfiles + production compose + CD workflow
|
|
29
|
+
|
|
30
|
+
## Setup Profiles
|
|
31
|
+
|
|
32
|
+
The scaffold wizard supports three profiles:
|
|
33
|
+
|
|
34
|
+
- **Recommended**: All app features plus Docker deployment assets and CD workflow.
|
|
35
|
+
- **Platform-First**: All app features, without deployment assets.
|
|
36
|
+
- **Custom**: Pick app features and deployment options independently.
|
|
28
37
|
|
|
29
38
|
## Requirements
|
|
30
39
|
|
package/dist/index.js
CHANGED
|
@@ -155,7 +155,7 @@ var REPLACEABLE_FILES = [
|
|
|
155
155
|
"README.md"
|
|
156
156
|
];
|
|
157
157
|
var DEFAULT_DESCRIPTION = "A full-stack TypeScript monorepo built with Blitzpack";
|
|
158
|
-
var
|
|
158
|
+
var APP_FEATURES = [
|
|
159
159
|
{
|
|
160
160
|
key: "testing",
|
|
161
161
|
name: "Testing",
|
|
@@ -170,11 +170,18 @@ var OPTIONAL_FEATURES = [
|
|
|
170
170
|
key: "uploads",
|
|
171
171
|
name: "File Uploads",
|
|
172
172
|
description: "S3 storage, upload routes, file components"
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
var DEPLOYMENT_FEATURES = [
|
|
176
|
+
{
|
|
177
|
+
key: "dockerDeploy",
|
|
178
|
+
name: "Docker Deployment",
|
|
179
|
+
description: "Dockerfiles for API/Web and production Docker Compose"
|
|
173
180
|
},
|
|
174
181
|
{
|
|
175
|
-
key: "
|
|
176
|
-
name: "
|
|
177
|
-
description: "
|
|
182
|
+
key: "ciCd",
|
|
183
|
+
name: "CD Workflow",
|
|
184
|
+
description: "GitHub Actions workflow to build and publish Docker images"
|
|
178
185
|
}
|
|
179
186
|
];
|
|
180
187
|
var FEATURE_EXCLUSIONS = {
|
|
@@ -218,7 +225,13 @@ var FEATURE_EXCLUSIONS = {
|
|
|
218
225
|
"packages/ui/src/file-upload-input.tsx",
|
|
219
226
|
"packages/types/src/upload.ts"
|
|
220
227
|
],
|
|
221
|
-
|
|
228
|
+
dockerDeploy: [
|
|
229
|
+
"apps/api/.dockerignore",
|
|
230
|
+
"apps/api/Dockerfile",
|
|
231
|
+
"apps/web/Dockerfile",
|
|
232
|
+
"deploy/docker"
|
|
233
|
+
],
|
|
234
|
+
ciCd: [".github/workflows/cd.yml"]
|
|
222
235
|
};
|
|
223
236
|
|
|
224
237
|
// src/utils.ts
|
|
@@ -397,16 +410,21 @@ async function promptFeatureSelection() {
|
|
|
397
410
|
{
|
|
398
411
|
type: "select",
|
|
399
412
|
name: "setupType",
|
|
400
|
-
message: "
|
|
413
|
+
message: "Project profile:",
|
|
401
414
|
choices: [
|
|
402
415
|
{
|
|
403
416
|
title: "Recommended",
|
|
404
|
-
description: "all features
|
|
417
|
+
description: "all app features + Docker deploy assets + CD workflow",
|
|
405
418
|
value: "recommended"
|
|
406
419
|
},
|
|
407
420
|
{
|
|
408
|
-
title: "
|
|
409
|
-
description: "
|
|
421
|
+
title: "Platform-First",
|
|
422
|
+
description: "all app features, no deployment assets",
|
|
423
|
+
value: "platform"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
title: "Custom",
|
|
427
|
+
description: "choose app and deployment features",
|
|
410
428
|
value: "customize"
|
|
411
429
|
}
|
|
412
430
|
],
|
|
@@ -427,21 +445,55 @@ async function promptFeatureSelection() {
|
|
|
427
445
|
testing: true,
|
|
428
446
|
admin: true,
|
|
429
447
|
uploads: true,
|
|
430
|
-
|
|
448
|
+
dockerDeploy: true,
|
|
449
|
+
ciCd: true
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if (setupType === "platform") {
|
|
453
|
+
return {
|
|
454
|
+
testing: true,
|
|
455
|
+
admin: true,
|
|
456
|
+
uploads: true,
|
|
457
|
+
dockerDeploy: false,
|
|
458
|
+
ciCd: false
|
|
431
459
|
};
|
|
432
460
|
}
|
|
433
|
-
const
|
|
461
|
+
const appFeatureChoices = APP_FEATURES.map((feature) => ({
|
|
462
|
+
title: feature.name,
|
|
463
|
+
description: feature.description,
|
|
464
|
+
value: feature.key,
|
|
465
|
+
selected: true
|
|
466
|
+
}));
|
|
467
|
+
const { selectedAppFeatures } = await prompts(
|
|
468
|
+
{
|
|
469
|
+
type: "multiselect",
|
|
470
|
+
name: "selectedAppFeatures",
|
|
471
|
+
message: "Select app features:",
|
|
472
|
+
choices: appFeatureChoices,
|
|
473
|
+
hint: "- Space to toggle, Enter to confirm",
|
|
474
|
+
instructions: false
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
onCancel: () => {
|
|
478
|
+
cancelled = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
if (cancelled) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const deploymentFeatureChoices = DEPLOYMENT_FEATURES.map((feature) => ({
|
|
434
486
|
title: feature.name,
|
|
435
487
|
description: feature.description,
|
|
436
488
|
value: feature.key,
|
|
437
489
|
selected: false
|
|
438
490
|
}));
|
|
439
|
-
const {
|
|
491
|
+
const { selectedDeploymentFeatures } = await prompts(
|
|
440
492
|
{
|
|
441
493
|
type: "multiselect",
|
|
442
|
-
name: "
|
|
443
|
-
message: "Select
|
|
444
|
-
choices:
|
|
494
|
+
name: "selectedDeploymentFeatures",
|
|
495
|
+
message: "Select deployment options (optional):",
|
|
496
|
+
choices: deploymentFeatureChoices,
|
|
445
497
|
hint: "- Space to toggle, Enter to confirm",
|
|
446
498
|
instructions: false
|
|
447
499
|
},
|
|
@@ -454,12 +506,24 @@ async function promptFeatureSelection() {
|
|
|
454
506
|
if (cancelled) {
|
|
455
507
|
return null;
|
|
456
508
|
}
|
|
457
|
-
const
|
|
509
|
+
const selectedApp = selectedAppFeatures || [];
|
|
510
|
+
const selectedDeployment = selectedDeploymentFeatures || [];
|
|
511
|
+
const includesCiCd = selectedDeployment.includes("ciCd");
|
|
512
|
+
const includesDockerDeploy = selectedDeployment.includes("dockerDeploy") || includesCiCd;
|
|
513
|
+
if (includesCiCd && !selectedDeployment.includes("dockerDeploy")) {
|
|
514
|
+
console.log();
|
|
515
|
+
console.log(
|
|
516
|
+
chalk3.dim(
|
|
517
|
+
" \u2139 CD workflow requires Docker deployment assets, enabling both."
|
|
518
|
+
)
|
|
519
|
+
);
|
|
520
|
+
}
|
|
458
521
|
return {
|
|
459
|
-
testing:
|
|
460
|
-
admin:
|
|
461
|
-
uploads:
|
|
462
|
-
|
|
522
|
+
testing: selectedApp.includes("testing"),
|
|
523
|
+
admin: selectedApp.includes("admin"),
|
|
524
|
+
uploads: selectedApp.includes("uploads"),
|
|
525
|
+
dockerDeploy: includesDockerDeploy,
|
|
526
|
+
ciCd: includesCiCd
|
|
463
527
|
};
|
|
464
528
|
}
|
|
465
529
|
async function promptAutomaticSetup() {
|
|
@@ -480,7 +544,7 @@ async function promptAutomaticSetup() {
|
|
|
480
544
|
const { runSetup } = await prompts({
|
|
481
545
|
type: "confirm",
|
|
482
546
|
name: "runSetup",
|
|
483
|
-
message: "Run
|
|
547
|
+
message: "Run local setup now? (start PostgreSQL with Docker + run migrations)",
|
|
484
548
|
initial: true
|
|
485
549
|
});
|
|
486
550
|
return runSetup || false;
|
|
@@ -546,6 +610,193 @@ async function countFiles(dir) {
|
|
|
546
610
|
// src/transform.ts
|
|
547
611
|
import fs2 from "fs-extra";
|
|
548
612
|
import path3 from "path";
|
|
613
|
+
|
|
614
|
+
// src/agent-doc-template.ts
|
|
615
|
+
var AGENT_DOC_TEMPLATE = `# CLAUDE.md
|
|
616
|
+
|
|
617
|
+
You are a Senior Full-stack Developer and an Expert in TypeScript, Next.js 16, React 19, Fastify, Prisma v7, PostgreSQL, TailwindCSS v5, and shadcn/ui. You are collaborating with a human developer on a production-ready full-stack application.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## Critical Requirements & Constraints
|
|
622
|
+
|
|
623
|
+
### Forbidden Behaviors
|
|
624
|
+
|
|
625
|
+
- NEVER create markdown files unless the user explicitly asks for it. This is very important
|
|
626
|
+
- NEVER create \`index.ts\` barrel files. This is a strict requirement
|
|
627
|
+
- AVOID writing comments in code unless absolutely necessary for non-obvious edge cases
|
|
628
|
+
|
|
629
|
+
### Workflow Requirements
|
|
630
|
+
|
|
631
|
+
- Make sure you aggressively prefer planning and waiting for user confirmation before writing code for medium to big tasks. This is a strict requirement
|
|
632
|
+
- If you are unsure about anything, ask the user for clarification. This is a strict requirement
|
|
633
|
+
- Prefer using commands or exec scripts (like \`pnpm dlx\`) for setup related tasks instead of making all the files manually. In case the command requires interactive input, ask the user to do it themselves and provide them with suitable guidance
|
|
634
|
+
|
|
635
|
+
### Collaboration Principles
|
|
636
|
+
|
|
637
|
+
- Always be unbiased towards the code, the user's preference and opinions. If you do not agree with the user, make it clear with them. Treat them like a peer
|
|
638
|
+
- Always mention alternative approaches to the user if you think it's necessary
|
|
639
|
+
- Do not be afraid of hurting the user's feelings or making them feel bad about their skills. Having well-written and maintainable code is significantly more important than cozying up to the user
|
|
640
|
+
- Always give a brief and compact summary of all the changes done
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Common Commands
|
|
645
|
+
|
|
646
|
+
### Development Commands
|
|
647
|
+
|
|
648
|
+
\`\`\`bash
|
|
649
|
+
pnpm dev
|
|
650
|
+
pnpm build
|
|
651
|
+
pnpm typecheck
|
|
652
|
+
pnpm lint
|
|
653
|
+
\`\`\`
|
|
654
|
+
|
|
655
|
+
<!-- @feature testing -->
|
|
656
|
+
\`\`\`bash
|
|
657
|
+
pnpm test
|
|
658
|
+
pnpm test:unit
|
|
659
|
+
pnpm test:integration
|
|
660
|
+
\`\`\`
|
|
661
|
+
<!-- @endfeature -->
|
|
662
|
+
|
|
663
|
+
### Individual Package Commands
|
|
664
|
+
|
|
665
|
+
\`\`\`bash
|
|
666
|
+
# Web (Next.js on port 3000)
|
|
667
|
+
cd apps/web
|
|
668
|
+
pnpm dev
|
|
669
|
+
pnpm typecheck
|
|
670
|
+
|
|
671
|
+
# API (Fastify on port 8080)
|
|
672
|
+
cd apps/api
|
|
673
|
+
pnpm dev
|
|
674
|
+
pnpm start
|
|
675
|
+
\`\`\`
|
|
676
|
+
|
|
677
|
+
### Database Commands (run from \`apps/api\`)
|
|
678
|
+
|
|
679
|
+
\`\`\`bash
|
|
680
|
+
pnpm db:generate # Generate Prisma Client
|
|
681
|
+
pnpm db:migrate # Create and apply migrations
|
|
682
|
+
pnpm db:push # Push schema changes (no migration files)
|
|
683
|
+
pnpm db:studio # Open Prisma Studio UI
|
|
684
|
+
pnpm db:seed # Seed database
|
|
685
|
+
\`\`\`
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## Coding Standards
|
|
690
|
+
|
|
691
|
+
### Code Style
|
|
692
|
+
|
|
693
|
+
- Type Definitions: Prefer \`interface\` for public-facing types and object shapes, \`type\` for unions, intersections, and computed types
|
|
694
|
+
- Use descriptive variable names with auxiliary verbs (\`isLoading\`, \`hasError\`, \`canSubmit\`)
|
|
695
|
+
- Favor default exports for pages and named exports for utilities or functions
|
|
696
|
+
- **IMPORTANT**: Always use import aliases for files in apps. For packages, use relative imports.
|
|
697
|
+
|
|
698
|
+
### Error Handling
|
|
699
|
+
|
|
700
|
+
- Use our custom error classes from \`packages/utils/src/errors.ts\`
|
|
701
|
+
- Provide user-friendly error messages and avoid leaking implementation details
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
## Project Architecture
|
|
706
|
+
|
|
707
|
+
### Packages
|
|
708
|
+
|
|
709
|
+
- All packages (\`@repo/packages-types\`, \`@repo/packages-utils\`) are consumed directly as TypeScript source files
|
|
710
|
+
- No build or watch required for these packages
|
|
711
|
+
|
|
712
|
+
### Environment
|
|
713
|
+
|
|
714
|
+
- Environment variables are managed per-app with Zod validation
|
|
715
|
+
- Web: Only \`NEXT_PUBLIC_*\` variables are exposed to the browser
|
|
716
|
+
- Prisma CLI commands are wrapped with \`dotenv-cli\` to read from \`.env.local\`
|
|
717
|
+
|
|
718
|
+
### Database
|
|
719
|
+
|
|
720
|
+
- The API uses Prisma 7 as the ORM with PostgreSQL
|
|
721
|
+
- Prisma queries are automatically logged based on \`LOG_LEVEL\`
|
|
722
|
+
|
|
723
|
+
### Type Safety & Validation
|
|
724
|
+
|
|
725
|
+
- The codebase emphasizes runtime and compile-time type safety with **Zod v4 as the single validation library** across the entire stack
|
|
726
|
+
- No class-validator
|
|
727
|
+
|
|
728
|
+
<!-- @feature testing -->
|
|
729
|
+
### Testing
|
|
730
|
+
|
|
731
|
+
Unit Tests: \`*.spec.ts\`
|
|
732
|
+
Integration Tests: \`*.integration.spec.ts\`
|
|
733
|
+
<!-- @endfeature -->
|
|
734
|
+
|
|
735
|
+
### Authentication
|
|
736
|
+
|
|
737
|
+
- Uses Better Auth
|
|
738
|
+
- When creating users with password auth, set \`accountId\` to the user's email (not user.id)
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## Web Architecture Patterns
|
|
743
|
+
|
|
744
|
+
### State Management
|
|
745
|
+
|
|
746
|
+
- TanStack Query (React Query): For API calls and data synchronization
|
|
747
|
+
- Jotai: For client-side global state (UI state, user preferences)
|
|
748
|
+
|
|
749
|
+
### API Integration
|
|
750
|
+
|
|
751
|
+
- API config centralized in \`apps/web/src/lib/api.ts\`
|
|
752
|
+
- **API Response Unwrapping**: The \`api.ts\` fetcher unwraps \`{ data: T }\` to \`T\` but preserves \`{ data: T[], pagination: {...} }\` responses intact
|
|
753
|
+
|
|
754
|
+
### Authentication & Protected Routes
|
|
755
|
+
|
|
756
|
+
The web app uses Better Auth for authentication with modern patterns:
|
|
757
|
+
|
|
758
|
+
- **Auth Client**: Configured in \`apps/web/src/lib/auth.ts\`
|
|
759
|
+
- Provides \`useSession()\` hook for accessing current user/session
|
|
760
|
+
- Handles sign in/out, session persistence via cookies
|
|
761
|
+
- **Protected Routes**: \`<ProtectedRoute>\` wrapper component for page-level protection
|
|
762
|
+
|
|
763
|
+
### UI Component Patterns
|
|
764
|
+
|
|
765
|
+
- Use Shadcn UI components as base, extend them before creating custom ones
|
|
766
|
+
- Implement thoughtful micro-interactions and hover states wherever needed
|
|
767
|
+
- Use Framer Motion for animations, Lucide React for icons
|
|
768
|
+
- Prefer using \`Skeleton\` components for loading states instead of spinners (especially for data fetching components)
|
|
769
|
+
- Always create a proper loading state for data fetching components.
|
|
770
|
+
- Always use \`cn\` helper for dynamic classnames
|
|
771
|
+
- Never write svg code. Always use existing icons from code or Lucide.
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## API Architecture Patterns
|
|
776
|
+
|
|
777
|
+
### Fastify Structure
|
|
778
|
+
|
|
779
|
+
- The Fastify API follows a plugin-based architecture
|
|
780
|
+
- Validation: Zod schemas directly in route definitions via \`fastify-type-provider-zod\`
|
|
781
|
+
- Dependency Injection: Manual DI via Fastify decorators
|
|
782
|
+
|
|
783
|
+
### Logging
|
|
784
|
+
|
|
785
|
+
- The API uses Pino for structured logging with request tracing
|
|
786
|
+
- Verbosity controlled by \`LOG_LEVEL\` env var: \`minimal\` | \`normal\` | \`detailed\` | \`verbose\`
|
|
787
|
+
- **IMPORTANT**: Never use \`console.log\` in the API. Use the logger service instead
|
|
788
|
+
|
|
789
|
+
**Log Methods:**
|
|
790
|
+
|
|
791
|
+
- \`logger.info(message, context?)\` - Standard information
|
|
792
|
+
- \`logger.error(message, error?, context?)\` - Errors with automatic Error serialization
|
|
793
|
+
- \`logger.warn(message, context?)\` - Warnings
|
|
794
|
+
- \`logger.debug(message, context?)\` - Debug information (detailed+ level)
|
|
795
|
+
- \`logger.trace(message, context?)\` - Trace logs (verbose level only)
|
|
796
|
+
- \`logger.perf(message, metrics)\` - Performance metrics
|
|
797
|
+
`;
|
|
798
|
+
|
|
799
|
+
// src/transform.ts
|
|
549
800
|
var TESTING_SCRIPTS = [
|
|
550
801
|
"test",
|
|
551
802
|
"test:unit",
|
|
@@ -564,6 +815,15 @@ var TESTING_ROOT_DEVDEPS = [
|
|
|
564
815
|
];
|
|
565
816
|
var TESTING_APP_DEVDEPS = ["vitest", "vite-tsconfig-paths"];
|
|
566
817
|
var UPLOADS_API_DEPS = ["@aws-sdk/client-s3", "sharp"];
|
|
818
|
+
var TESTING_DIR_NAMES = /* @__PURE__ */ new Set(["__tests__", "test", "tests"]);
|
|
819
|
+
var TESTING_FILE_PATTERNS = [
|
|
820
|
+
/\.test\.[^/]+$/i,
|
|
821
|
+
/\.spec\.[^/]+$/i,
|
|
822
|
+
/^vitest(?:\.[^.]+)*\.(?:[cm]?[jt]sx?)$/i,
|
|
823
|
+
/^test-config\.(?:[cm]?[jt]sx?)$/i
|
|
824
|
+
];
|
|
825
|
+
var TS_CONFIG_FILE_PATTERN = /^tsconfig(?:\.[^.]+)?\.json$/;
|
|
826
|
+
var AGENT_DOC_TARGETS = ["CLAUDE.md", "AGENTS.md"];
|
|
567
827
|
var MARKER_FILES = [
|
|
568
828
|
"apps/api/src/app.ts",
|
|
569
829
|
"apps/api/src/plugins/services.ts",
|
|
@@ -573,22 +833,23 @@ function stripFeatureBlocks(content, disabledFeatures) {
|
|
|
573
833
|
const lines = content.split("\n");
|
|
574
834
|
const result = [];
|
|
575
835
|
let skipUntilEnd = false;
|
|
576
|
-
let currentFeature = null;
|
|
577
836
|
for (const line of lines) {
|
|
578
|
-
const featureStart = line.match(
|
|
579
|
-
|
|
837
|
+
const featureStart = line.match(
|
|
838
|
+
/^\s*(?:\/\/|<!--)\s*@feature\s+(\w+)\s*(?:-->)?\s*$/
|
|
839
|
+
);
|
|
840
|
+
const featureEnd = line.match(
|
|
841
|
+
/^\s*(?:\/\/|<!--)\s*@endfeature\s*(?:-->)?\s*$/
|
|
842
|
+
);
|
|
580
843
|
if (featureStart) {
|
|
581
844
|
const feature = featureStart[1];
|
|
582
845
|
if (disabledFeatures.includes(feature)) {
|
|
583
846
|
skipUntilEnd = true;
|
|
584
|
-
currentFeature = feature;
|
|
585
847
|
}
|
|
586
848
|
continue;
|
|
587
849
|
}
|
|
588
850
|
if (featureEnd) {
|
|
589
851
|
if (skipUntilEnd) {
|
|
590
852
|
skipUntilEnd = false;
|
|
591
|
-
currentFeature = null;
|
|
592
853
|
}
|
|
593
854
|
continue;
|
|
594
855
|
}
|
|
@@ -736,6 +997,8 @@ async function applyFeatureTransforms(targetDir, features) {
|
|
|
736
997
|
if (!features.testing) disabledFeatures.push("testing");
|
|
737
998
|
if (!features.admin) disabledFeatures.push("admin");
|
|
738
999
|
if (!features.uploads) disabledFeatures.push("uploads");
|
|
1000
|
+
if (!features.dockerDeploy) disabledFeatures.push("dockerDeploy");
|
|
1001
|
+
if (!features.ciCd) disabledFeatures.push("ciCd");
|
|
739
1002
|
for (const relativePath of MARKER_FILES) {
|
|
740
1003
|
const filePath = path3.join(targetDir, relativePath);
|
|
741
1004
|
if (await fs2.pathExists(filePath)) {
|
|
@@ -748,8 +1011,12 @@ async function applyFeatureTransforms(targetDir, features) {
|
|
|
748
1011
|
if (!features.testing) {
|
|
749
1012
|
await transformForNoTesting(targetDir);
|
|
750
1013
|
}
|
|
1014
|
+
await transformAgentDocs(targetDir, disabledFeatures);
|
|
751
1015
|
}
|
|
752
1016
|
async function transformForNoTesting(targetDir) {
|
|
1017
|
+
await removeTestingArtifacts(targetDir);
|
|
1018
|
+
await stripTestingFromWorkspacePackageJson(targetDir);
|
|
1019
|
+
await stripTestingFromTsConfigs(targetDir);
|
|
753
1020
|
const turboPath = path3.join(targetDir, "turbo.json");
|
|
754
1021
|
if (await fs2.pathExists(turboPath)) {
|
|
755
1022
|
const content = await fs2.readFile(turboPath, "utf-8");
|
|
@@ -759,6 +1026,7 @@ async function transformForNoTesting(targetDir) {
|
|
|
759
1026
|
delete turbo.tasks?.["test:integration"];
|
|
760
1027
|
delete turbo.tasks?.["test:watch"];
|
|
761
1028
|
delete turbo.tasks?.["test:coverage"];
|
|
1029
|
+
delete turbo.tasks?.["test:parallel"];
|
|
762
1030
|
await fs2.writeFile(turboPath, JSON.stringify(turbo, null, 2) + "\n");
|
|
763
1031
|
}
|
|
764
1032
|
const huskyPath = path3.join(targetDir, ".husky/pre-push");
|
|
@@ -766,6 +1034,149 @@ async function transformForNoTesting(targetDir) {
|
|
|
766
1034
|
await fs2.writeFile(huskyPath, "pnpm typecheck\n");
|
|
767
1035
|
}
|
|
768
1036
|
}
|
|
1037
|
+
function isTestingDependency(name) {
|
|
1038
|
+
return name === "vitest" || name.startsWith("@vitest/") || name.startsWith("@testing-library/") || name === "jsdom" || name === "vite-tsconfig-paths";
|
|
1039
|
+
}
|
|
1040
|
+
function isTestingIncludeEntry(value) {
|
|
1041
|
+
return value.includes("__tests__") || value.includes("/test") || value.includes("test/") || value.includes("tests/") || value.includes(".test.") || value.includes(".spec.");
|
|
1042
|
+
}
|
|
1043
|
+
async function removeTestingArtifacts(currentDir) {
|
|
1044
|
+
const entries = await fs2.readdir(currentDir);
|
|
1045
|
+
for (const entry of entries) {
|
|
1046
|
+
if (entry === ".git" || entry === "node_modules") {
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
const fullPath = path3.join(currentDir, entry);
|
|
1050
|
+
const stat = await fs2.stat(fullPath);
|
|
1051
|
+
if (stat.isDirectory()) {
|
|
1052
|
+
if (TESTING_DIR_NAMES.has(entry)) {
|
|
1053
|
+
await fs2.remove(fullPath);
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
await removeTestingArtifacts(fullPath);
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (TESTING_FILE_PATTERNS.some((pattern) => pattern.test(entry))) {
|
|
1060
|
+
await fs2.remove(fullPath);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
async function stripTestingFromWorkspacePackageJson(currentDir) {
|
|
1065
|
+
const entries = await fs2.readdir(currentDir);
|
|
1066
|
+
for (const entry of entries) {
|
|
1067
|
+
if (entry === ".git" || entry === "node_modules") {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
const fullPath = path3.join(currentDir, entry);
|
|
1071
|
+
const stat = await fs2.stat(fullPath);
|
|
1072
|
+
if (stat.isDirectory()) {
|
|
1073
|
+
await stripTestingFromWorkspacePackageJson(fullPath);
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
if (entry !== "package.json") {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
const content = await fs2.readFile(fullPath, "utf-8");
|
|
1080
|
+
const pkg = JSON.parse(content);
|
|
1081
|
+
let changed = false;
|
|
1082
|
+
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
1083
|
+
for (const scriptName of Object.keys(pkg.scripts)) {
|
|
1084
|
+
if (scriptName === "test" || scriptName.startsWith("test:")) {
|
|
1085
|
+
delete pkg.scripts[scriptName];
|
|
1086
|
+
changed = true;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const dependencySections = [
|
|
1091
|
+
"dependencies",
|
|
1092
|
+
"devDependencies",
|
|
1093
|
+
"peerDependencies",
|
|
1094
|
+
"optionalDependencies"
|
|
1095
|
+
];
|
|
1096
|
+
for (const section of dependencySections) {
|
|
1097
|
+
if (!pkg[section] || typeof pkg[section] !== "object") {
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
for (const depName of Object.keys(pkg[section])) {
|
|
1101
|
+
if (isTestingDependency(depName)) {
|
|
1102
|
+
delete pkg[section][depName];
|
|
1103
|
+
changed = true;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if ("vitest" in pkg) {
|
|
1108
|
+
delete pkg.vitest;
|
|
1109
|
+
changed = true;
|
|
1110
|
+
}
|
|
1111
|
+
if (changed) {
|
|
1112
|
+
await fs2.writeFile(fullPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
async function stripTestingFromTsConfigs(currentDir) {
|
|
1117
|
+
const entries = await fs2.readdir(currentDir);
|
|
1118
|
+
for (const entry of entries) {
|
|
1119
|
+
if (entry === ".git" || entry === "node_modules") {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
const fullPath = path3.join(currentDir, entry);
|
|
1123
|
+
const stat = await fs2.stat(fullPath);
|
|
1124
|
+
if (stat.isDirectory()) {
|
|
1125
|
+
await stripTestingFromTsConfigs(fullPath);
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
if (!TS_CONFIG_FILE_PATTERN.test(entry)) {
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
const content = await fs2.readFile(fullPath, "utf-8");
|
|
1132
|
+
const tsconfig = JSON.parse(content);
|
|
1133
|
+
let changed = false;
|
|
1134
|
+
const compilerOptions = tsconfig.compilerOptions;
|
|
1135
|
+
if (compilerOptions && typeof compilerOptions === "object") {
|
|
1136
|
+
if (Array.isArray(compilerOptions.types)) {
|
|
1137
|
+
const nextTypes = compilerOptions.types.filter(
|
|
1138
|
+
(value) => typeof value === "string" && !value.includes("vitest") && !value.includes("@testing-library")
|
|
1139
|
+
);
|
|
1140
|
+
if (nextTypes.length !== compilerOptions.types.length) {
|
|
1141
|
+
compilerOptions.types = nextTypes;
|
|
1142
|
+
changed = true;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (compilerOptions.paths && typeof compilerOptions.paths === "object") {
|
|
1146
|
+
if ("@test/*" in compilerOptions.paths) {
|
|
1147
|
+
delete compilerOptions.paths["@test/*"];
|
|
1148
|
+
changed = true;
|
|
1149
|
+
}
|
|
1150
|
+
if ("@tests/*" in compilerOptions.paths) {
|
|
1151
|
+
delete compilerOptions.paths["@tests/*"];
|
|
1152
|
+
changed = true;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
if (Array.isArray(tsconfig.include)) {
|
|
1157
|
+
const nextInclude = tsconfig.include.filter(
|
|
1158
|
+
(value) => typeof value === "string" && !isTestingIncludeEntry(value)
|
|
1159
|
+
);
|
|
1160
|
+
if (nextInclude.length !== tsconfig.include.length) {
|
|
1161
|
+
tsconfig.include = nextInclude;
|
|
1162
|
+
changed = true;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (changed) {
|
|
1166
|
+
await fs2.writeFile(fullPath, JSON.stringify(tsconfig, null, 2) + "\n");
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
async function transformAgentDocs(targetDir, disabledFeatures) {
|
|
1171
|
+
let content = stripFeatureBlocks(AGENT_DOC_TEMPLATE, disabledFeatures);
|
|
1172
|
+
content = cleanEmptyLines(content).trimEnd() + "\n";
|
|
1173
|
+
for (const fileName of AGENT_DOC_TARGETS) {
|
|
1174
|
+
const filePath = path3.join(targetDir, fileName);
|
|
1175
|
+
const heading = fileName === "AGENTS.md" ? "# AGENTS.md" : "# CLAUDE.md";
|
|
1176
|
+
const fileContent = content.replace(/^#\s+CLAUDE\.md/m, heading);
|
|
1177
|
+
await fs2.writeFile(filePath, fileContent, "utf-8");
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
769
1180
|
|
|
770
1181
|
// src/commands/create.ts
|
|
771
1182
|
var ENV_FILES = [
|
|
@@ -816,6 +1227,12 @@ function printDryRun(options) {
|
|
|
816
1227
|
console.log(
|
|
817
1228
|
` ${featureStatus(options.features.uploads)} File Uploads ${chalk4.dim("(S3 storage, upload routes)")}`
|
|
818
1229
|
);
|
|
1230
|
+
console.log(
|
|
1231
|
+
` ${featureStatus(options.features.dockerDeploy)} Docker Deployment ${chalk4.dim("(API/Web Dockerfiles, production compose)")}`
|
|
1232
|
+
);
|
|
1233
|
+
console.log(
|
|
1234
|
+
` ${featureStatus(options.features.ciCd)} CD Workflow ${chalk4.dim("(GitHub Actions image build/push)")}`
|
|
1235
|
+
);
|
|
819
1236
|
console.log();
|
|
820
1237
|
console.log(chalk4.bold(" Would run:"));
|
|
821
1238
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-blitzpack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Create a new Blitzpack project - full-stack TypeScript monorepo with Next.js and Fastify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"create-blitzpack": "
|
|
7
|
+
"create-blitzpack": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist"
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"license": "MIT",
|
|
48
48
|
"repository": {
|
|
49
49
|
"type": "git",
|
|
50
|
-
"url": "https://github.com/CarboxyDev/blitzpack"
|
|
50
|
+
"url": "git+https://github.com/CarboxyDev/blitzpack.git"
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://github.com/CarboxyDev/blitzpack",
|
|
53
53
|
"engines": {
|