create-better-fullstack 1.1.9 → 1.1.10
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/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-B07FiCRd.mjs → src-BkL9XJbn.mjs} +491 -31
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-
|
|
2
|
+
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-BkL9XJbn.mjs";
|
|
3
3
|
|
|
4
4
|
export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
|
@@ -625,6 +625,67 @@ function handleError(error, fallbackMessage) {
|
|
|
625
625
|
process.exit(1);
|
|
626
626
|
}
|
|
627
627
|
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/utils/error-formatter.ts
|
|
630
|
+
function getCategoryTitle(category) {
|
|
631
|
+
return {
|
|
632
|
+
incompatibility: "Incompatible Options",
|
|
633
|
+
"invalid-selection": "Invalid Selection",
|
|
634
|
+
"missing-requirement": "Missing Requirement",
|
|
635
|
+
constraint: "Constraint Violation"
|
|
636
|
+
}[category];
|
|
637
|
+
}
|
|
638
|
+
function displayStructuredError(error) {
|
|
639
|
+
if (isSilent()) throw new CLIError(error.message);
|
|
640
|
+
const lines = [];
|
|
641
|
+
lines.push(pc.bold(pc.red(getCategoryTitle(error.category))));
|
|
642
|
+
lines.push("");
|
|
643
|
+
lines.push(error.message);
|
|
644
|
+
if (error.provided && Object.keys(error.provided).length > 0) {
|
|
645
|
+
lines.push("");
|
|
646
|
+
lines.push(pc.dim("You provided:"));
|
|
647
|
+
for (const [key, value] of Object.entries(error.provided)) {
|
|
648
|
+
const displayValue = Array.isArray(value) ? value.join(", ") : value;
|
|
649
|
+
lines.push(` ${pc.cyan("--" + key)} ${displayValue}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (error.suggestions.length > 0) {
|
|
653
|
+
lines.push("");
|
|
654
|
+
lines.push(pc.dim("Suggestions:"));
|
|
655
|
+
for (const suggestion of error.suggestions) lines.push(` ${pc.green("•")} ${suggestion}`);
|
|
656
|
+
}
|
|
657
|
+
consola.box({
|
|
658
|
+
title: pc.red("Error"),
|
|
659
|
+
message: lines.join("\n"),
|
|
660
|
+
style: { borderColor: "red" }
|
|
661
|
+
});
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
function incompatibilityError(opts) {
|
|
665
|
+
return displayStructuredError({
|
|
666
|
+
category: "incompatibility",
|
|
667
|
+
...opts
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
function invalidSelectionError(opts) {
|
|
671
|
+
return displayStructuredError({
|
|
672
|
+
category: "invalid-selection",
|
|
673
|
+
...opts
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
function missingRequirementError(opts) {
|
|
677
|
+
return displayStructuredError({
|
|
678
|
+
category: "missing-requirement",
|
|
679
|
+
...opts
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
function constraintError(opts) {
|
|
683
|
+
return displayStructuredError({
|
|
684
|
+
category: "constraint",
|
|
685
|
+
...opts
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
628
689
|
//#endregion
|
|
629
690
|
//#region src/utils/compatibility-rules.ts
|
|
630
691
|
function isWebFrontend(value) {
|
|
@@ -638,8 +699,16 @@ function splitFrontends(values = []) {
|
|
|
638
699
|
}
|
|
639
700
|
function ensureSingleWebAndNative(frontends) {
|
|
640
701
|
const { web, native } = splitFrontends(frontends);
|
|
641
|
-
if (web.length > 1)
|
|
642
|
-
|
|
702
|
+
if (web.length > 1) invalidSelectionError({
|
|
703
|
+
message: "Only one web framework can be selected per project.",
|
|
704
|
+
provided: { frontend: web },
|
|
705
|
+
suggestions: ["Keep one web framework and remove the others", "Use separate projects for multiple web frameworks"]
|
|
706
|
+
});
|
|
707
|
+
if (native.length > 1) invalidSelectionError({
|
|
708
|
+
message: "Only one native framework can be selected per project.",
|
|
709
|
+
provided: { frontend: native },
|
|
710
|
+
suggestions: ["Keep one native framework and remove the others", "Choose: native-bare, native-uniwind, or native-unistyles"]
|
|
711
|
+
});
|
|
643
712
|
}
|
|
644
713
|
const FULLSTACK_FRONTENDS$1 = [
|
|
645
714
|
"next",
|
|
@@ -663,11 +732,51 @@ const WORKERS_COMPATIBLE_BACKENDS = [
|
|
|
663
732
|
"fets"
|
|
664
733
|
];
|
|
665
734
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
666
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend))
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
735
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend)) incompatibilityError({
|
|
736
|
+
message: "Cloudflare Workers runtime requires a compatible backend.",
|
|
737
|
+
provided: {
|
|
738
|
+
runtime: "workers",
|
|
739
|
+
backend: config.backend
|
|
740
|
+
},
|
|
741
|
+
suggestions: [
|
|
742
|
+
"Use --backend hono",
|
|
743
|
+
"Use --backend nitro",
|
|
744
|
+
"Use --backend fets",
|
|
745
|
+
"Choose a different runtime (node, bun)"
|
|
746
|
+
]
|
|
747
|
+
});
|
|
748
|
+
if (providedFlags.has("backend") && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend) && config.runtime === "workers") incompatibilityError({
|
|
749
|
+
message: `Backend '${config.backend}' is not compatible with Workers runtime.`,
|
|
750
|
+
provided: {
|
|
751
|
+
backend: config.backend,
|
|
752
|
+
runtime: "workers"
|
|
753
|
+
},
|
|
754
|
+
suggestions: ["Use --backend hono, --backend nitro, or --backend fets", "Choose a different runtime (node, bun)"]
|
|
755
|
+
});
|
|
756
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") incompatibilityError({
|
|
757
|
+
message: "Cloudflare Workers runtime is not compatible with MongoDB.",
|
|
758
|
+
provided: {
|
|
759
|
+
runtime: "workers",
|
|
760
|
+
database: "mongodb"
|
|
761
|
+
},
|
|
762
|
+
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
763
|
+
});
|
|
764
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") incompatibilityError({
|
|
765
|
+
message: "Cloudflare Workers runtime is not compatible with Docker setup.",
|
|
766
|
+
provided: {
|
|
767
|
+
runtime: "workers",
|
|
768
|
+
"db-setup": "docker"
|
|
769
|
+
},
|
|
770
|
+
suggestions: ["Use --db-setup d1 for SQLite", "Choose a different runtime (node, bun)"]
|
|
771
|
+
});
|
|
772
|
+
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") incompatibilityError({
|
|
773
|
+
message: "MongoDB is not compatible with Cloudflare Workers runtime.",
|
|
774
|
+
provided: {
|
|
775
|
+
database: "mongodb",
|
|
776
|
+
runtime: "workers"
|
|
777
|
+
},
|
|
778
|
+
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
779
|
+
});
|
|
671
780
|
}
|
|
672
781
|
function validateApiFrontendCompatibility(api, frontends = [], astroIntegration) {
|
|
673
782
|
const includesNuxt = frontends.includes("nuxt");
|
|
@@ -678,12 +787,66 @@ function validateApiFrontendCompatibility(api, frontends = [], astroIntegration)
|
|
|
678
787
|
const includesAngular = frontends.includes("angular");
|
|
679
788
|
const includesRedwood = frontends.includes("redwood");
|
|
680
789
|
const includesFresh = frontends.includes("fresh");
|
|
681
|
-
if ((includesNuxt || includesSvelte || includesSolid) && (api === "trpc" || api === "ts-rest" || api === "garph"))
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
790
|
+
if ((includesNuxt || includesSvelte || includesSolid) && (api === "trpc" || api === "ts-rest" || api === "garph")) {
|
|
791
|
+
const apiName = api === "trpc" ? "tRPC" : api === "ts-rest" ? "ts-rest" : "garph";
|
|
792
|
+
const incompatibleFrontend = includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid";
|
|
793
|
+
incompatibilityError({
|
|
794
|
+
message: `${apiName} API requires React-based frontends.`,
|
|
795
|
+
provided: {
|
|
796
|
+
api,
|
|
797
|
+
frontend: incompatibleFrontend
|
|
798
|
+
},
|
|
799
|
+
suggestions: [
|
|
800
|
+
"Use --api orpc (works with all frontends)",
|
|
801
|
+
"Use --api none",
|
|
802
|
+
"Choose next, react-router, or tanstack-start"
|
|
803
|
+
]
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
if (includesQwik && api && api !== "none") incompatibilityError({
|
|
807
|
+
message: "Qwik has built-in server capabilities and doesn't support external APIs.",
|
|
808
|
+
provided: {
|
|
809
|
+
api,
|
|
810
|
+
frontend: "qwik"
|
|
811
|
+
},
|
|
812
|
+
suggestions: ["Use --api none with Qwik"]
|
|
813
|
+
});
|
|
814
|
+
if (includesAngular && api && api !== "none") incompatibilityError({
|
|
815
|
+
message: "Angular has built-in HttpClient and doesn't support external APIs.",
|
|
816
|
+
provided: {
|
|
817
|
+
api,
|
|
818
|
+
frontend: "angular"
|
|
819
|
+
},
|
|
820
|
+
suggestions: ["Use --api none with Angular"]
|
|
821
|
+
});
|
|
822
|
+
if (includesRedwood && api && api !== "none") incompatibilityError({
|
|
823
|
+
message: "RedwoodJS has built-in GraphQL API and doesn't support external APIs.",
|
|
824
|
+
provided: {
|
|
825
|
+
api,
|
|
826
|
+
frontend: "redwood"
|
|
827
|
+
},
|
|
828
|
+
suggestions: ["Use --api none with RedwoodJS"]
|
|
829
|
+
});
|
|
830
|
+
if (includesFresh && api && api !== "none") incompatibilityError({
|
|
831
|
+
message: "Fresh has built-in server capabilities and doesn't support external APIs.",
|
|
832
|
+
provided: {
|
|
833
|
+
api,
|
|
834
|
+
frontend: "fresh"
|
|
835
|
+
},
|
|
836
|
+
suggestions: ["Use --api none with Fresh"]
|
|
837
|
+
});
|
|
838
|
+
if (includesAstro && astroIntegration && astroIntegration !== "react" && (api === "trpc" || api === "ts-rest" || api === "garph")) incompatibilityError({
|
|
839
|
+
message: `${api === "trpc" ? "tRPC" : api === "ts-rest" ? "ts-rest" : "garph"} API requires React integration with Astro.`,
|
|
840
|
+
provided: {
|
|
841
|
+
api,
|
|
842
|
+
"astro-integration": astroIntegration
|
|
843
|
+
},
|
|
844
|
+
suggestions: [
|
|
845
|
+
"Use --api orpc (works with all Astro integrations)",
|
|
846
|
+
"Use --api none",
|
|
847
|
+
"Use --astro-integration react"
|
|
848
|
+
]
|
|
849
|
+
});
|
|
687
850
|
}
|
|
688
851
|
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
689
852
|
if (backend === "convex" && frontend === "solid") return false;
|
|
@@ -817,10 +980,27 @@ function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astro
|
|
|
817
980
|
"next"
|
|
818
981
|
].includes(f))) return;
|
|
819
982
|
}
|
|
820
|
-
if (!compatibility.frontends.includes("astro"))
|
|
983
|
+
if (!compatibility.frontends.includes("astro")) {
|
|
984
|
+
const integrationName = astroIntegration || "none";
|
|
985
|
+
incompatibilityError({
|
|
986
|
+
message: `UI library '${uiLibrary}' requires React.`,
|
|
987
|
+
provided: {
|
|
988
|
+
"ui-library": uiLibrary,
|
|
989
|
+
"astro-integration": integrationName
|
|
990
|
+
},
|
|
991
|
+
suggestions: ["Use --astro-integration react", "Choose a different UI library (daisyui, ark-ui)"]
|
|
992
|
+
});
|
|
993
|
+
}
|
|
821
994
|
return;
|
|
822
995
|
}
|
|
823
|
-
if (!compatibility.frontends.includes(webFrontend))
|
|
996
|
+
if (!compatibility.frontends.includes(webFrontend)) incompatibilityError({
|
|
997
|
+
message: `UI library '${uiLibrary}' is not compatible with '${webFrontend}'.`,
|
|
998
|
+
provided: {
|
|
999
|
+
"ui-library": uiLibrary,
|
|
1000
|
+
frontend: webFrontend
|
|
1001
|
+
},
|
|
1002
|
+
suggestions: [`Supported frontends: ${compatibility.frontends.join(", ")}`, "Choose a different UI library"]
|
|
1003
|
+
});
|
|
824
1004
|
}
|
|
825
1005
|
/**
|
|
826
1006
|
* Validates that a UI library is compatible with the selected CSS framework
|
|
@@ -3674,21 +3854,244 @@ function validateArrayOptions(options) {
|
|
|
3674
3854
|
validateNoneExclusivity(options.examples, "examples");
|
|
3675
3855
|
}
|
|
3676
3856
|
|
|
3857
|
+
//#endregion
|
|
3858
|
+
//#region src/utils/peer-dependency-conflicts.ts
|
|
3859
|
+
/**
|
|
3860
|
+
* Known peer dependency conflicts.
|
|
3861
|
+
*
|
|
3862
|
+
* Add new conflicts here as they are discovered.
|
|
3863
|
+
* The validator will check these against the user's selected options.
|
|
3864
|
+
*/
|
|
3865
|
+
const PEER_DEPENDENCY_CONFLICTS = [
|
|
3866
|
+
{
|
|
3867
|
+
id: "redux-toolkit-react19",
|
|
3868
|
+
description: "redux-toolkit with React 19 may have peer dependency warnings",
|
|
3869
|
+
packages: ["@reduxjs/toolkit", "react-redux"],
|
|
3870
|
+
severity: "warning",
|
|
3871
|
+
resolution: "Consider using zustand or jotai for React 19 projects, or ensure react-redux v9+",
|
|
3872
|
+
triggeredBy: [{
|
|
3873
|
+
optionKey: "stateManagement",
|
|
3874
|
+
values: ["redux-toolkit"]
|
|
3875
|
+
}],
|
|
3876
|
+
conflictsWithOptions: [{
|
|
3877
|
+
optionKey: "frontend",
|
|
3878
|
+
values: ["next"]
|
|
3879
|
+
}]
|
|
3880
|
+
},
|
|
3881
|
+
{
|
|
3882
|
+
id: "effect-schema-zod-overlap",
|
|
3883
|
+
description: "@effect/schema and zod both provide validation - may cause confusion",
|
|
3884
|
+
packages: ["@effect/schema", "zod"],
|
|
3885
|
+
severity: "warning",
|
|
3886
|
+
resolution: "Use --validation effect-schema with Effect, or --effect none with Zod",
|
|
3887
|
+
triggeredBy: [{
|
|
3888
|
+
optionKey: "effect",
|
|
3889
|
+
values: ["effect-full"]
|
|
3890
|
+
}],
|
|
3891
|
+
conflictsWithOptions: [{
|
|
3892
|
+
optionKey: "validation",
|
|
3893
|
+
values: ["zod"]
|
|
3894
|
+
}]
|
|
3895
|
+
},
|
|
3896
|
+
{
|
|
3897
|
+
id: "prisma-d1-version",
|
|
3898
|
+
description: "Prisma with D1 requires Prisma 5.x+ and specific adapter setup",
|
|
3899
|
+
packages: ["prisma", "@prisma/adapter-d1"],
|
|
3900
|
+
severity: "warning",
|
|
3901
|
+
resolution: "Consider Drizzle ORM for simpler D1 integration",
|
|
3902
|
+
triggeredBy: [{
|
|
3903
|
+
optionKey: "orm",
|
|
3904
|
+
values: ["prisma"]
|
|
3905
|
+
}],
|
|
3906
|
+
conflictsWithOptions: [{
|
|
3907
|
+
optionKey: "dbSetup",
|
|
3908
|
+
values: ["d1"]
|
|
3909
|
+
}]
|
|
3910
|
+
},
|
|
3911
|
+
{
|
|
3912
|
+
id: "biome-linting-overlap",
|
|
3913
|
+
description: "Biome includes linting/formatting - other linting addons are redundant",
|
|
3914
|
+
packages: [
|
|
3915
|
+
"@biomejs/biome",
|
|
3916
|
+
"eslint",
|
|
3917
|
+
"prettier"
|
|
3918
|
+
],
|
|
3919
|
+
severity: "warning",
|
|
3920
|
+
resolution: "Choose either Biome (all-in-one) or other linting tools, not both",
|
|
3921
|
+
triggeredBy: [{
|
|
3922
|
+
optionKey: "addons",
|
|
3923
|
+
values: ["biome"]
|
|
3924
|
+
}],
|
|
3925
|
+
conflictsWithOptions: [{
|
|
3926
|
+
optionKey: "addons",
|
|
3927
|
+
values: ["ultracite", "oxlint"]
|
|
3928
|
+
}]
|
|
3929
|
+
},
|
|
3930
|
+
{
|
|
3931
|
+
id: "framer-motion-react19",
|
|
3932
|
+
description: "framer-motion versions <12 may have React 19 issues",
|
|
3933
|
+
packages: ["framer-motion"],
|
|
3934
|
+
severity: "warning",
|
|
3935
|
+
resolution: "The CLI uses motion (framer-motion v12+) which supports React 19",
|
|
3936
|
+
triggeredBy: [{
|
|
3937
|
+
optionKey: "animation",
|
|
3938
|
+
values: ["framer-motion"]
|
|
3939
|
+
}],
|
|
3940
|
+
conflictsWithOptions: [{
|
|
3941
|
+
optionKey: "frontend",
|
|
3942
|
+
values: ["next"]
|
|
3943
|
+
}]
|
|
3944
|
+
}
|
|
3945
|
+
];
|
|
3946
|
+
|
|
3947
|
+
//#endregion
|
|
3948
|
+
//#region src/utils/peer-dependency-validator.ts
|
|
3949
|
+
/**
|
|
3950
|
+
* Peer dependency conflict validator.
|
|
3951
|
+
*
|
|
3952
|
+
* Checks project configuration against known conflicts and provides
|
|
3953
|
+
* warnings or errors before project creation begins.
|
|
3954
|
+
*/
|
|
3955
|
+
/**
|
|
3956
|
+
* Checks if a config value matches any of the specified values.
|
|
3957
|
+
*/
|
|
3958
|
+
function matchesValue(configValue, values) {
|
|
3959
|
+
if (configValue === void 0 || configValue === null) return false;
|
|
3960
|
+
if (Array.isArray(configValue)) return values.some((v) => configValue.includes(v));
|
|
3961
|
+
return values.includes(configValue);
|
|
3962
|
+
}
|
|
3963
|
+
/**
|
|
3964
|
+
* Checks the configuration against known peer dependency conflicts.
|
|
3965
|
+
*/
|
|
3966
|
+
function checkPeerDependencyConflicts(config) {
|
|
3967
|
+
const errors = [];
|
|
3968
|
+
const warnings = [];
|
|
3969
|
+
for (const conflict of PEER_DEPENDENCY_CONFLICTS) {
|
|
3970
|
+
if (!conflict.triggeredBy.some((trigger) => {
|
|
3971
|
+
const configValue = config[trigger.optionKey];
|
|
3972
|
+
return matchesValue(configValue, trigger.values);
|
|
3973
|
+
})) continue;
|
|
3974
|
+
if (conflict.conflictsWithOptions.some((opt) => {
|
|
3975
|
+
const configValue = config[opt.optionKey];
|
|
3976
|
+
return matchesValue(configValue, opt.values);
|
|
3977
|
+
})) if (conflict.severity === "error") errors.push(conflict);
|
|
3978
|
+
else warnings.push(conflict);
|
|
3979
|
+
}
|
|
3980
|
+
return {
|
|
3981
|
+
errors,
|
|
3982
|
+
warnings
|
|
3983
|
+
};
|
|
3984
|
+
}
|
|
3985
|
+
/**
|
|
3986
|
+
* Prints a warning message for a dependency conflict.
|
|
3987
|
+
*/
|
|
3988
|
+
function warnDependencyConflict(message, resolution) {
|
|
3989
|
+
if (isSilent()) return;
|
|
3990
|
+
consola.warn(pc.yellow(`Peer Dependency Warning: ${message}`));
|
|
3991
|
+
consola.log(pc.dim(` → ${resolution}`));
|
|
3992
|
+
}
|
|
3993
|
+
/**
|
|
3994
|
+
* Handles conflict results by printing warnings and exiting on errors.
|
|
3995
|
+
*/
|
|
3996
|
+
function handleConflictResult(result) {
|
|
3997
|
+
for (const warning of result.warnings) warnDependencyConflict(warning.description, warning.resolution);
|
|
3998
|
+
if (result.errors.length > 0) exitWithError(`Peer Dependency Conflict:\n${result.errors.map((e) => `${e.description}\n → ${e.resolution}`).join("\n\n")}`);
|
|
3999
|
+
}
|
|
4000
|
+
/**
|
|
4001
|
+
* Validates peer dependencies for the given configuration.
|
|
4002
|
+
* Prints warnings and exits on errors.
|
|
4003
|
+
*/
|
|
4004
|
+
function validatePeerDependencies(config) {
|
|
4005
|
+
handleConflictResult(checkPeerDependencyConflicts(config));
|
|
4006
|
+
}
|
|
4007
|
+
|
|
3677
4008
|
//#endregion
|
|
3678
4009
|
//#region src/utils/config-validation.ts
|
|
3679
4010
|
function validateDatabaseOrmAuth(cfg, flags) {
|
|
3680
4011
|
const db = cfg.database;
|
|
3681
4012
|
const orm = cfg.orm;
|
|
3682
4013
|
const has = (k) => flags ? flags.has(k) : true;
|
|
3683
|
-
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb")
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
if (has("orm") && has("database") && orm
|
|
4014
|
+
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") incompatibilityError({
|
|
4015
|
+
message: "Mongoose ORM requires MongoDB database.",
|
|
4016
|
+
provided: {
|
|
4017
|
+
orm: "mongoose",
|
|
4018
|
+
database: db || "none"
|
|
4019
|
+
},
|
|
4020
|
+
suggestions: ["Use --database mongodb", "Choose a different ORM (drizzle, prisma)"]
|
|
4021
|
+
});
|
|
4022
|
+
if (has("orm") && has("database") && orm === "drizzle" && db === "mongodb") incompatibilityError({
|
|
4023
|
+
message: "Drizzle ORM does not support MongoDB.",
|
|
4024
|
+
provided: {
|
|
4025
|
+
orm: "drizzle",
|
|
4026
|
+
database: "mongodb"
|
|
4027
|
+
},
|
|
4028
|
+
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
4029
|
+
});
|
|
4030
|
+
if (has("orm") && has("database") && orm === "typeorm" && db === "mongodb") incompatibilityError({
|
|
4031
|
+
message: "TypeORM does not support MongoDB in Better Fullstack.",
|
|
4032
|
+
provided: {
|
|
4033
|
+
orm: "typeorm",
|
|
4034
|
+
database: "mongodb"
|
|
4035
|
+
},
|
|
4036
|
+
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
4037
|
+
});
|
|
4038
|
+
if (has("orm") && has("database") && orm === "kysely" && db === "mongodb") incompatibilityError({
|
|
4039
|
+
message: "Kysely does not support MongoDB.",
|
|
4040
|
+
provided: {
|
|
4041
|
+
orm: "kysely",
|
|
4042
|
+
database: "mongodb"
|
|
4043
|
+
},
|
|
4044
|
+
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
4045
|
+
});
|
|
4046
|
+
if (has("orm") && has("database") && orm === "mikroorm" && db === "mongodb") incompatibilityError({
|
|
4047
|
+
message: "MikroORM does not support MongoDB in Better Fullstack.",
|
|
4048
|
+
provided: {
|
|
4049
|
+
orm: "mikroorm",
|
|
4050
|
+
database: "mongodb"
|
|
4051
|
+
},
|
|
4052
|
+
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
4053
|
+
});
|
|
4054
|
+
if (has("orm") && has("database") && orm === "sequelize" && db === "mongodb") incompatibilityError({
|
|
4055
|
+
message: "Sequelize does not support MongoDB.",
|
|
4056
|
+
provided: {
|
|
4057
|
+
orm: "sequelize",
|
|
4058
|
+
database: "mongodb"
|
|
4059
|
+
},
|
|
4060
|
+
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
4061
|
+
});
|
|
4062
|
+
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") incompatibilityError({
|
|
4063
|
+
message: "MongoDB only works with Mongoose or Prisma ORM.",
|
|
4064
|
+
provided: {
|
|
4065
|
+
database: "mongodb",
|
|
4066
|
+
orm
|
|
4067
|
+
},
|
|
4068
|
+
suggestions: ["Use --orm mongoose", "Use --orm prisma"]
|
|
4069
|
+
});
|
|
4070
|
+
if (has("database") && has("orm") && db && db !== "none" && orm === "none") missingRequirementError({
|
|
4071
|
+
message: "Database selection requires an ORM.",
|
|
4072
|
+
provided: {
|
|
4073
|
+
database: db,
|
|
4074
|
+
orm: "none"
|
|
4075
|
+
},
|
|
4076
|
+
suggestions: [
|
|
4077
|
+
"Use --orm drizzle (recommended)",
|
|
4078
|
+
"Use --orm prisma",
|
|
4079
|
+
"Use --orm mongoose (MongoDB only)"
|
|
4080
|
+
]
|
|
4081
|
+
});
|
|
4082
|
+
if (has("orm") && has("database") && orm && orm !== "none" && db === "none") missingRequirementError({
|
|
4083
|
+
message: "ORM selection requires a database.",
|
|
4084
|
+
provided: {
|
|
4085
|
+
orm,
|
|
4086
|
+
database: "none"
|
|
4087
|
+
},
|
|
4088
|
+
suggestions: [
|
|
4089
|
+
"Use --database postgres",
|
|
4090
|
+
"Use --database sqlite",
|
|
4091
|
+
"Use --database mysql",
|
|
4092
|
+
"Set --orm none"
|
|
4093
|
+
]
|
|
4094
|
+
});
|
|
3692
4095
|
}
|
|
3693
4096
|
function validateDatabaseSetup(config, providedFlags) {
|
|
3694
4097
|
const { dbSetup, database, runtime } = config;
|
|
@@ -3739,12 +4142,54 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
3739
4142
|
const { backend } = config;
|
|
3740
4143
|
if (backend !== "convex") return;
|
|
3741
4144
|
const has = (k) => providedFlags.has(k);
|
|
3742
|
-
if (has("runtime") && config.runtime !== "none")
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
4145
|
+
if (has("runtime") && config.runtime !== "none") constraintError({
|
|
4146
|
+
message: "Convex backend manages its own runtime.",
|
|
4147
|
+
provided: {
|
|
4148
|
+
backend: "convex",
|
|
4149
|
+
runtime: config.runtime || ""
|
|
4150
|
+
},
|
|
4151
|
+
suggestions: ["Remove --runtime flag", "Set --runtime none"]
|
|
4152
|
+
});
|
|
4153
|
+
if (has("database") && config.database !== "none") constraintError({
|
|
4154
|
+
message: "Convex backend has its own built-in database.",
|
|
4155
|
+
provided: {
|
|
4156
|
+
backend: "convex",
|
|
4157
|
+
database: config.database || ""
|
|
4158
|
+
},
|
|
4159
|
+
suggestions: ["Remove --database flag", "Set --database none"]
|
|
4160
|
+
});
|
|
4161
|
+
if (has("orm") && config.orm !== "none") constraintError({
|
|
4162
|
+
message: "Convex backend has its own data layer (no ORM needed).",
|
|
4163
|
+
provided: {
|
|
4164
|
+
backend: "convex",
|
|
4165
|
+
orm: config.orm || ""
|
|
4166
|
+
},
|
|
4167
|
+
suggestions: ["Remove --orm flag", "Set --orm none"]
|
|
4168
|
+
});
|
|
4169
|
+
if (has("api") && config.api !== "none") constraintError({
|
|
4170
|
+
message: "Convex backend has its own built-in API layer.",
|
|
4171
|
+
provided: {
|
|
4172
|
+
backend: "convex",
|
|
4173
|
+
api: config.api || ""
|
|
4174
|
+
},
|
|
4175
|
+
suggestions: ["Remove --api flag", "Set --api none"]
|
|
4176
|
+
});
|
|
4177
|
+
if (has("dbSetup") && config.dbSetup !== "none") constraintError({
|
|
4178
|
+
message: "Convex backend manages its own database infrastructure.",
|
|
4179
|
+
provided: {
|
|
4180
|
+
backend: "convex",
|
|
4181
|
+
"db-setup": config.dbSetup || ""
|
|
4182
|
+
},
|
|
4183
|
+
suggestions: ["Remove --db-setup flag", "Set --db-setup none"]
|
|
4184
|
+
});
|
|
4185
|
+
if (has("serverDeploy") && config.serverDeploy !== "none") constraintError({
|
|
4186
|
+
message: "Convex backend has its own deployment platform.",
|
|
4187
|
+
provided: {
|
|
4188
|
+
backend: "convex",
|
|
4189
|
+
"server-deploy": config.serverDeploy || ""
|
|
4190
|
+
},
|
|
4191
|
+
suggestions: ["Remove --server-deploy flag", "Set --server-deploy none"]
|
|
4192
|
+
});
|
|
3748
4193
|
if (has("auth") && config.auth === "better-auth") {
|
|
3749
4194
|
const supportedFrontends = [
|
|
3750
4195
|
"tanstack-router",
|
|
@@ -3754,7 +4199,20 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
3754
4199
|
"native-uniwind",
|
|
3755
4200
|
"native-unistyles"
|
|
3756
4201
|
];
|
|
3757
|
-
if (!config.frontend?.some((f) => supportedFrontends.includes(f)))
|
|
4202
|
+
if (!config.frontend?.some((f) => supportedFrontends.includes(f))) incompatibilityError({
|
|
4203
|
+
message: "Better-Auth with Convex requires specific frontends.",
|
|
4204
|
+
provided: {
|
|
4205
|
+
backend: "convex",
|
|
4206
|
+
auth: "better-auth",
|
|
4207
|
+
frontend: config.frontend || []
|
|
4208
|
+
},
|
|
4209
|
+
suggestions: [
|
|
4210
|
+
"Use --frontend tanstack-router",
|
|
4211
|
+
"Use --frontend tanstack-start",
|
|
4212
|
+
"Use --frontend next",
|
|
4213
|
+
"Use a native frontend"
|
|
4214
|
+
]
|
|
4215
|
+
});
|
|
3758
4216
|
}
|
|
3759
4217
|
}
|
|
3760
4218
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
@@ -3851,6 +4309,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
3851
4309
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
3852
4310
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
3853
4311
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
4312
|
+
validatePeerDependencies(config);
|
|
3854
4313
|
}
|
|
3855
4314
|
function validateConfigForProgrammaticUse(config) {
|
|
3856
4315
|
try {
|
|
@@ -3863,6 +4322,7 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
3863
4322
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
|
|
3864
4323
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
3865
4324
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
4325
|
+
validatePeerDependencies(config);
|
|
3866
4326
|
} catch (error) {
|
|
3867
4327
|
if (error instanceof Error) throw error;
|
|
3868
4328
|
throw new Error(String(error));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"description": "A CLI-first toolkit for building Full Stack applications. Skip the configuration. Ship the code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"prepublishOnly": "npm run build"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@better-fullstack/template-generator": "^1.1.
|
|
77
|
-
"@better-fullstack/types": "^1.1.
|
|
76
|
+
"@better-fullstack/template-generator": "^1.1.10",
|
|
77
|
+
"@better-fullstack/types": "^1.1.10",
|
|
78
78
|
"@clack/core": "^0.5.0",
|
|
79
79
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
80
80
|
"@orpc/server": "^1.13.0",
|