bunkit-cli 1.3.2 → 1.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/dist/index.js +1331 -1051
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14851,39 +14851,89 @@ var init_monorepo = __esm(() => {
|
|
|
14851
14851
|
init_fs();
|
|
14852
14852
|
});
|
|
14853
14853
|
|
|
14854
|
+
// ../core/src/presets/custom.ts
|
|
14855
|
+
function getPresetsDir() {
|
|
14856
|
+
return join(process.env.HOME || process.env.USERPROFILE || ".", ".bunkit");
|
|
14857
|
+
}
|
|
14858
|
+
function getPresetsFile() {
|
|
14859
|
+
return join(getPresetsDir(), "presets.json");
|
|
14860
|
+
}
|
|
14861
|
+
async function loadCustomPresets() {
|
|
14862
|
+
try {
|
|
14863
|
+
const presetsDir = getPresetsDir();
|
|
14864
|
+
await ensureDirectory(presetsDir);
|
|
14865
|
+
const content = await readFile(getPresetsFile());
|
|
14866
|
+
return JSON.parse(content);
|
|
14867
|
+
} catch (_error) {
|
|
14868
|
+
return {};
|
|
14869
|
+
}
|
|
14870
|
+
}
|
|
14871
|
+
async function saveCustomPreset(preset) {
|
|
14872
|
+
await ensureDirectory(getPresetsDir());
|
|
14873
|
+
const presets = await loadCustomPresets();
|
|
14874
|
+
const existing = presets[preset.name];
|
|
14875
|
+
presets[preset.name] = {
|
|
14876
|
+
...preset,
|
|
14877
|
+
createdAt: existing?.createdAt || new Date().toISOString(),
|
|
14878
|
+
updatedAt: new Date().toISOString()
|
|
14879
|
+
};
|
|
14880
|
+
await writeFile(getPresetsFile(), JSON.stringify(presets, null, 2));
|
|
14881
|
+
}
|
|
14882
|
+
async function deleteCustomPreset(name) {
|
|
14883
|
+
const presets = await loadCustomPresets();
|
|
14884
|
+
if (!presets[name]) {
|
|
14885
|
+
return false;
|
|
14886
|
+
}
|
|
14887
|
+
delete presets[name];
|
|
14888
|
+
await writeFile(getPresetsFile(), JSON.stringify(presets, null, 2));
|
|
14889
|
+
return true;
|
|
14890
|
+
}
|
|
14891
|
+
async function getCustomPreset(name) {
|
|
14892
|
+
const presets = await loadCustomPresets();
|
|
14893
|
+
return presets[name] || null;
|
|
14894
|
+
}
|
|
14895
|
+
async function listCustomPresets() {
|
|
14896
|
+
const presets = await loadCustomPresets();
|
|
14897
|
+
return Object.values(presets);
|
|
14898
|
+
}
|
|
14899
|
+
var init_custom2 = __esm(() => {
|
|
14900
|
+
init_dist();
|
|
14901
|
+
init_fs();
|
|
14902
|
+
});
|
|
14903
|
+
|
|
14854
14904
|
// ../core/src/presets/registry.ts
|
|
14855
14905
|
class PresetRegistry {
|
|
14856
14906
|
static aliasMap = null;
|
|
14857
14907
|
static getAliasMap() {
|
|
14858
|
-
if (!
|
|
14859
|
-
|
|
14908
|
+
if (!PresetRegistry.aliasMap) {
|
|
14909
|
+
PresetRegistry.aliasMap = new Map;
|
|
14860
14910
|
for (const [name, definition] of Object.entries(PRESET_DEFINITIONS)) {
|
|
14861
|
-
|
|
14911
|
+
PresetRegistry.aliasMap.set(name, name);
|
|
14862
14912
|
for (const alias of definition.aliases) {
|
|
14863
|
-
|
|
14913
|
+
PresetRegistry.aliasMap.set(alias, name);
|
|
14864
14914
|
}
|
|
14865
14915
|
}
|
|
14866
14916
|
}
|
|
14867
|
-
return
|
|
14917
|
+
return PresetRegistry.aliasMap;
|
|
14868
14918
|
}
|
|
14869
14919
|
static normalize(input) {
|
|
14870
|
-
const map =
|
|
14920
|
+
const map = PresetRegistry.getAliasMap();
|
|
14871
14921
|
return map.get(input) || null;
|
|
14872
14922
|
}
|
|
14873
14923
|
static get(input) {
|
|
14874
|
-
const normalized =
|
|
14924
|
+
const normalized = PresetRegistry.normalize(input);
|
|
14875
14925
|
if (!normalized)
|
|
14876
14926
|
return null;
|
|
14877
14927
|
return PRESET_DEFINITIONS[normalized];
|
|
14878
14928
|
}
|
|
14879
14929
|
static hasCapability(input, capability) {
|
|
14880
|
-
const preset =
|
|
14930
|
+
const preset = PresetRegistry.get(input);
|
|
14881
14931
|
if (!preset)
|
|
14882
14932
|
return false;
|
|
14883
14933
|
return preset.capabilities[capability];
|
|
14884
14934
|
}
|
|
14885
14935
|
static isMonorepo(input) {
|
|
14886
|
-
return
|
|
14936
|
+
return PresetRegistry.hasCapability(input, "isMonorepo");
|
|
14887
14937
|
}
|
|
14888
14938
|
static getSelectOptions() {
|
|
14889
14939
|
return Object.values(PRESET_DEFINITIONS).map((preset) => ({
|
|
@@ -14896,10 +14946,10 @@ class PresetRegistry {
|
|
|
14896
14946
|
return Object.values(PRESET_DEFINITIONS).filter((preset) => preset.capabilities[capability]);
|
|
14897
14947
|
}
|
|
14898
14948
|
static isValid(input) {
|
|
14899
|
-
return
|
|
14949
|
+
return PresetRegistry.normalize(input) !== null;
|
|
14900
14950
|
}
|
|
14901
14951
|
static getAllValidNames() {
|
|
14902
|
-
return Array.from(
|
|
14952
|
+
return Array.from(PresetRegistry.getAliasMap().keys());
|
|
14903
14953
|
}
|
|
14904
14954
|
}
|
|
14905
14955
|
var PRESET_DEFINITIONS;
|
|
@@ -15097,14 +15147,14 @@ var init_registry = __esm(() => {
|
|
|
15097
15147
|
autoprefixer: "^10.4.23",
|
|
15098
15148
|
"radix-ui": "^1.4.3",
|
|
15099
15149
|
"@radix-ui/react-slot": "^1.2.4",
|
|
15100
|
-
"@base-ui/react": "^1.
|
|
15101
|
-
shadcn: "^3.
|
|
15150
|
+
"@base-ui/react": "^1.2.0",
|
|
15151
|
+
shadcn: "^3.8.5",
|
|
15102
15152
|
"class-variance-authority": "^0.7.1",
|
|
15103
15153
|
clsx: "^2.1.1",
|
|
15104
15154
|
"tailwind-merge": "^3.4.0",
|
|
15105
15155
|
"tw-animate-css": "^1.4.0",
|
|
15106
|
-
"@phosphor-icons/react": "^2.1.10",
|
|
15107
15156
|
"iconoir-react": "^7.11.0",
|
|
15157
|
+
"@phosphor-icons/react": "^2.1.10",
|
|
15108
15158
|
ultracite: "^6.4.2",
|
|
15109
15159
|
"@biomejs/biome": "^2.3.10",
|
|
15110
15160
|
vitest: "^4.0.16",
|
|
@@ -15158,14 +15208,14 @@ var init_registry = __esm(() => {
|
|
|
15158
15208
|
autoprefixer: "^10.4.23",
|
|
15159
15209
|
"radix-ui": "^1.4.3",
|
|
15160
15210
|
"@radix-ui/react-slot": "^1.2.4",
|
|
15161
|
-
"@base-ui/react": "^1.
|
|
15162
|
-
shadcn: "^3.
|
|
15211
|
+
"@base-ui/react": "^1.2.0",
|
|
15212
|
+
shadcn: "^3.8.5",
|
|
15163
15213
|
"class-variance-authority": "^0.7.1",
|
|
15164
15214
|
clsx: "^2.1.1",
|
|
15165
15215
|
"tailwind-merge": "^3.4.0",
|
|
15166
15216
|
"tw-animate-css": "^1.4.0",
|
|
15167
|
-
"@phosphor-icons/react": "^2.1.10",
|
|
15168
15217
|
"iconoir-react": "^7.11.0",
|
|
15218
|
+
"@phosphor-icons/react": "^2.1.10",
|
|
15169
15219
|
ultracite: "^6.4.2",
|
|
15170
15220
|
"@biomejs/biome": "^2.3.10",
|
|
15171
15221
|
vitest: "^4.0.16",
|
|
@@ -15221,14 +15271,14 @@ var init_registry = __esm(() => {
|
|
|
15221
15271
|
autoprefixer: "^10.4.23",
|
|
15222
15272
|
"radix-ui": "^1.4.3",
|
|
15223
15273
|
"@radix-ui/react-slot": "^1.2.4",
|
|
15224
|
-
"@base-ui/react": "^1.
|
|
15225
|
-
shadcn: "^3.
|
|
15274
|
+
"@base-ui/react": "^1.2.0",
|
|
15275
|
+
shadcn: "^3.8.5",
|
|
15226
15276
|
"class-variance-authority": "^0.7.1",
|
|
15227
15277
|
clsx: "^2.1.1",
|
|
15228
15278
|
"tailwind-merge": "^3.4.0",
|
|
15229
15279
|
"tw-animate-css": "^1.4.0",
|
|
15230
|
-
"@phosphor-icons/react": "^2.1.10",
|
|
15231
15280
|
"iconoir-react": "^7.11.0",
|
|
15281
|
+
"@phosphor-icons/react": "^2.1.10",
|
|
15232
15282
|
ultracite: "^6.4.2",
|
|
15233
15283
|
"@biomejs/biome": "^2.3.10",
|
|
15234
15284
|
vitest: "^4.0.16",
|
|
@@ -15240,60 +15290,10 @@ var init_registry = __esm(() => {
|
|
|
15240
15290
|
};
|
|
15241
15291
|
});
|
|
15242
15292
|
|
|
15243
|
-
// ../core/src/presets/custom.ts
|
|
15244
|
-
function getPresetsDir() {
|
|
15245
|
-
return join(process.env.HOME || process.env.USERPROFILE || ".", ".bunkit");
|
|
15246
|
-
}
|
|
15247
|
-
function getPresetsFile() {
|
|
15248
|
-
return join(getPresetsDir(), "presets.json");
|
|
15249
|
-
}
|
|
15250
|
-
async function loadCustomPresets() {
|
|
15251
|
-
try {
|
|
15252
|
-
const presetsDir = getPresetsDir();
|
|
15253
|
-
await ensureDirectory(presetsDir);
|
|
15254
|
-
const content = await readFile(getPresetsFile());
|
|
15255
|
-
return JSON.parse(content);
|
|
15256
|
-
} catch (_error) {
|
|
15257
|
-
return {};
|
|
15258
|
-
}
|
|
15259
|
-
}
|
|
15260
|
-
async function saveCustomPreset(preset) {
|
|
15261
|
-
await ensureDirectory(getPresetsDir());
|
|
15262
|
-
const presets = await loadCustomPresets();
|
|
15263
|
-
const existing = presets[preset.name];
|
|
15264
|
-
presets[preset.name] = {
|
|
15265
|
-
...preset,
|
|
15266
|
-
createdAt: existing?.createdAt || new Date().toISOString(),
|
|
15267
|
-
updatedAt: new Date().toISOString()
|
|
15268
|
-
};
|
|
15269
|
-
await writeFile(getPresetsFile(), JSON.stringify(presets, null, 2));
|
|
15270
|
-
}
|
|
15271
|
-
async function deleteCustomPreset(name) {
|
|
15272
|
-
const presets = await loadCustomPresets();
|
|
15273
|
-
if (!presets[name]) {
|
|
15274
|
-
return false;
|
|
15275
|
-
}
|
|
15276
|
-
delete presets[name];
|
|
15277
|
-
await writeFile(getPresetsFile(), JSON.stringify(presets, null, 2));
|
|
15278
|
-
return true;
|
|
15279
|
-
}
|
|
15280
|
-
async function getCustomPreset(name) {
|
|
15281
|
-
const presets = await loadCustomPresets();
|
|
15282
|
-
return presets[name] || null;
|
|
15283
|
-
}
|
|
15284
|
-
async function listCustomPresets() {
|
|
15285
|
-
const presets = await loadCustomPresets();
|
|
15286
|
-
return Object.values(presets);
|
|
15287
|
-
}
|
|
15288
|
-
var init_custom2 = __esm(() => {
|
|
15289
|
-
init_dist();
|
|
15290
|
-
init_fs();
|
|
15291
|
-
});
|
|
15292
|
-
|
|
15293
15293
|
// ../core/src/presets/index.ts
|
|
15294
15294
|
var init_presets = __esm(() => {
|
|
15295
|
-
init_registry();
|
|
15296
15295
|
init_custom2();
|
|
15296
|
+
init_registry();
|
|
15297
15297
|
});
|
|
15298
15298
|
|
|
15299
15299
|
// ../core/src/project.ts
|
|
@@ -15465,6 +15465,7 @@ function createTemplateContext(config) {
|
|
|
15465
15465
|
shadcnMenuAccent: config.shadcnMenuAccent,
|
|
15466
15466
|
shadcnMenuColor: config.shadcnMenuColor,
|
|
15467
15467
|
shadcnRadius: config.shadcnRadius,
|
|
15468
|
+
shadcnRtl: config.shadcnRtl,
|
|
15468
15469
|
supabasePreset: config.supabasePreset,
|
|
15469
15470
|
supabaseFeatures: config.supabaseFeatures,
|
|
15470
15471
|
supabaseWithDrizzle: config.supabaseWithDrizzle
|
|
@@ -19444,6 +19445,17 @@ var init_zod = __esm(() => {
|
|
|
19444
19445
|
});
|
|
19445
19446
|
|
|
19446
19447
|
// ../core/src/types.ts
|
|
19448
|
+
function inferShadcnBase(style) {
|
|
19449
|
+
if (style?.startsWith("base-")) {
|
|
19450
|
+
return "base-ui";
|
|
19451
|
+
}
|
|
19452
|
+
return "radix";
|
|
19453
|
+
}
|
|
19454
|
+
function isModernShadcnStyle(style) {
|
|
19455
|
+
if (!style)
|
|
19456
|
+
return false;
|
|
19457
|
+
return style.startsWith("radix-") || style.startsWith("base-");
|
|
19458
|
+
}
|
|
19447
19459
|
var ProjectConfigSchema, FeatureConfigSchema;
|
|
19448
19460
|
var init_types2 = __esm(() => {
|
|
19449
19461
|
init_zod();
|
|
@@ -19490,13 +19502,27 @@ var init_types2 = __esm(() => {
|
|
|
19490
19502
|
tsStrictness: exports_external.enum(["strict", "moderate", "loose"]).default("strict"),
|
|
19491
19503
|
uiLibrary: exports_external.enum(["shadcn", "none"]).optional(),
|
|
19492
19504
|
cssFramework: exports_external.enum(["tailwind", "vanilla", "css-modules"]).optional(),
|
|
19493
|
-
shadcnStyle: exports_external.enum([
|
|
19505
|
+
shadcnStyle: exports_external.enum([
|
|
19506
|
+
"radix-maia",
|
|
19507
|
+
"radix-vega",
|
|
19508
|
+
"radix-nova",
|
|
19509
|
+
"radix-lyra",
|
|
19510
|
+
"radix-mira",
|
|
19511
|
+
"base-maia",
|
|
19512
|
+
"base-vega",
|
|
19513
|
+
"base-nova",
|
|
19514
|
+
"base-lyra",
|
|
19515
|
+
"base-mira",
|
|
19516
|
+
"new-york",
|
|
19517
|
+
"default"
|
|
19518
|
+
]).optional(),
|
|
19494
19519
|
shadcnBase: exports_external.enum(["radix", "base-ui"]).optional(),
|
|
19495
19520
|
shadcnBaseColor: exports_external.enum(["neutral", "gray", "zinc", "stone", "slate"]).optional(),
|
|
19496
19521
|
shadcnIconLibrary: exports_external.enum(["phosphor", "lucide", "iconoir"]).optional(),
|
|
19497
19522
|
shadcnMenuAccent: exports_external.enum(["subtle", "bold"]).optional(),
|
|
19498
19523
|
shadcnMenuColor: exports_external.enum(["default", "muted"]).optional(),
|
|
19499
19524
|
shadcnRadius: exports_external.string().optional(),
|
|
19525
|
+
shadcnRtl: exports_external.boolean().optional(),
|
|
19500
19526
|
testing: exports_external.enum(["bun-test", "vitest", "none"]).default("bun-test"),
|
|
19501
19527
|
docker: exports_external.boolean().default(false),
|
|
19502
19528
|
cicd: exports_external.boolean().default(false),
|
|
@@ -19571,6 +19597,7 @@ __export(exports_src, {
|
|
|
19571
19597
|
logger: () => logger,
|
|
19572
19598
|
loadCustomPresets: () => loadCustomPresets,
|
|
19573
19599
|
listCustomPresets: () => listCustomPresets,
|
|
19600
|
+
isModernShadcnStyle: () => isModernShadcnStyle,
|
|
19574
19601
|
isGitRepository: () => isGitRepository,
|
|
19575
19602
|
isGitAvailable: () => isGitAvailable,
|
|
19576
19603
|
isDirectoryEmpty: () => isDirectoryEmpty,
|
|
@@ -19578,6 +19605,7 @@ __export(exports_src, {
|
|
|
19578
19605
|
installDevDependencies: () => installDevDependencies,
|
|
19579
19606
|
installDependencies: () => installDependencies,
|
|
19580
19607
|
initGit: () => initGit,
|
|
19608
|
+
inferShadcnBase: () => inferShadcnBase,
|
|
19581
19609
|
getWorkspaceName: () => getWorkspaceName,
|
|
19582
19610
|
getRootPackageJson: () => getRootPackageJson,
|
|
19583
19611
|
getProjectName: () => getProjectName,
|
|
@@ -21850,11 +21878,13 @@ async function writeMonorepoRootPackageJson(projectPath, projectName, catalog, o
|
|
|
21850
21878
|
});
|
|
21851
21879
|
}
|
|
21852
21880
|
async function writeNextjsAppPackageJson(appPath, scopeName, appName, options = {}) {
|
|
21881
|
+
const iconLibrary = options.shadcnIconLibrary || "iconoir";
|
|
21882
|
+
const iconPackageName = iconLibrary === "iconoir" ? "iconoir-react" : iconLibrary === "phosphor" ? "@phosphor-icons/react" : "lucide-react";
|
|
21853
21883
|
const dependencies = {
|
|
21854
21884
|
react: "catalog:",
|
|
21855
21885
|
"react-dom": "catalog:",
|
|
21856
21886
|
next: "catalog:",
|
|
21857
|
-
|
|
21887
|
+
[iconPackageName]: "catalog:"
|
|
21858
21888
|
};
|
|
21859
21889
|
if (options.usesTypes) {
|
|
21860
21890
|
dependencies[`@${scopeName}/types`] = "workspace:*";
|
|
@@ -21916,7 +21946,23 @@ async function writeHonoApiPackageJson(appPath, scopeName, options = {}) {
|
|
|
21916
21946
|
}
|
|
21917
21947
|
});
|
|
21918
21948
|
}
|
|
21919
|
-
async function writeUiPackageJson(packagePath, scopeName) {
|
|
21949
|
+
async function writeUiPackageJson(packagePath, scopeName, options = {}) {
|
|
21950
|
+
const shadcnBase = options.shadcnBase || "radix";
|
|
21951
|
+
const iconLibrary = options.shadcnIconLibrary || "iconoir";
|
|
21952
|
+
const uiFoundationDeps = {};
|
|
21953
|
+
if (shadcnBase === "base-ui") {
|
|
21954
|
+
uiFoundationDeps["@base-ui/react"] = "catalog:";
|
|
21955
|
+
} else {
|
|
21956
|
+
uiFoundationDeps["radix-ui"] = "catalog:";
|
|
21957
|
+
}
|
|
21958
|
+
const iconDeps = {};
|
|
21959
|
+
if (iconLibrary === "iconoir") {
|
|
21960
|
+
iconDeps["iconoir-react"] = "catalog:";
|
|
21961
|
+
} else if (iconLibrary === "phosphor") {
|
|
21962
|
+
iconDeps["@phosphor-icons/react"] = "catalog:";
|
|
21963
|
+
} else {
|
|
21964
|
+
iconDeps["lucide-react"] = "catalog:";
|
|
21965
|
+
}
|
|
21920
21966
|
await writePackageJson(packagePath, {
|
|
21921
21967
|
name: `@${scopeName}/ui`,
|
|
21922
21968
|
version: "0.0.0",
|
|
@@ -21936,11 +21982,13 @@ async function writeUiPackageJson(packagePath, scopeName) {
|
|
|
21936
21982
|
lint: "tsc --noEmit"
|
|
21937
21983
|
},
|
|
21938
21984
|
dependencies: {
|
|
21939
|
-
|
|
21985
|
+
...uiFoundationDeps,
|
|
21940
21986
|
"class-variance-authority": "catalog:",
|
|
21941
21987
|
clsx: "catalog:",
|
|
21942
21988
|
"tailwind-merge": "catalog:",
|
|
21943
|
-
|
|
21989
|
+
...iconDeps,
|
|
21990
|
+
"tw-animate-css": "catalog:",
|
|
21991
|
+
shadcn: "catalog:",
|
|
21944
21992
|
tailwindcss: "catalog:",
|
|
21945
21993
|
"@tailwindcss/postcss": "catalog:",
|
|
21946
21994
|
postcss: "catalog:"
|
|
@@ -28711,9 +28759,10 @@ function generateModernThemeCSS(baseColor, customRadius) {
|
|
|
28711
28759
|
async function setupShadcnWeb(projectPath, context) {
|
|
28712
28760
|
await ensureDirectory(join(projectPath, "src/components/ui"));
|
|
28713
28761
|
await ensureDirectory(join(projectPath, "src/lib"));
|
|
28714
|
-
const style = context.shadcnStyle || "
|
|
28762
|
+
const style = context.shadcnStyle || "radix-maia";
|
|
28715
28763
|
const baseColor = context.shadcnBaseColor || "zinc";
|
|
28716
28764
|
const radius = context.shadcnRadius || "0.625rem";
|
|
28765
|
+
const iconLibrary = context.shadcnIconLibrary || "iconoir";
|
|
28717
28766
|
const componentsJson = {
|
|
28718
28767
|
$schema: "https://ui.shadcn.com/schema.json",
|
|
28719
28768
|
style,
|
|
@@ -28725,7 +28774,7 @@ async function setupShadcnWeb(projectPath, context) {
|
|
|
28725
28774
|
baseColor,
|
|
28726
28775
|
cssVariables: true
|
|
28727
28776
|
},
|
|
28728
|
-
iconLibrary
|
|
28777
|
+
iconLibrary,
|
|
28729
28778
|
aliases: {
|
|
28730
28779
|
components: "@/components",
|
|
28731
28780
|
utils: "@/lib/utils",
|
|
@@ -28734,6 +28783,9 @@ async function setupShadcnWeb(projectPath, context) {
|
|
|
28734
28783
|
hooks: "@/hooks"
|
|
28735
28784
|
}
|
|
28736
28785
|
};
|
|
28786
|
+
if (context.shadcnRtl) {
|
|
28787
|
+
componentsJson.rtl = true;
|
|
28788
|
+
}
|
|
28737
28789
|
await writeFile(join(projectPath, "components.json"), JSON.stringify(componentsJson, null, 2));
|
|
28738
28790
|
const utilsContent = `import { clsx, type ClassValue } from 'clsx';
|
|
28739
28791
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -28751,20 +28803,36 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
28751
28803
|
if (!packageJson.dependencies) {
|
|
28752
28804
|
packageJson.dependencies = {};
|
|
28753
28805
|
}
|
|
28754
|
-
|
|
28806
|
+
const shadcnBase = inferShadcnBase(context.shadcnStyle);
|
|
28807
|
+
if (shadcnBase === "base-ui") {
|
|
28808
|
+
packageJson.dependencies["@base-ui/react"] = "catalog:";
|
|
28809
|
+
} else {
|
|
28810
|
+
packageJson.dependencies["radix-ui"] = "catalog:";
|
|
28811
|
+
}
|
|
28755
28812
|
packageJson.dependencies["class-variance-authority"] = "catalog:";
|
|
28756
28813
|
packageJson.dependencies.clsx = "catalog:";
|
|
28757
28814
|
packageJson.dependencies["tailwind-merge"] = "catalog:";
|
|
28758
|
-
|
|
28815
|
+
const iconPackageName = iconLibrary === "iconoir" ? "iconoir-react" : iconLibrary === "phosphor" ? "@phosphor-icons/react" : "lucide-react";
|
|
28816
|
+
packageJson.dependencies[iconPackageName] = "catalog:";
|
|
28759
28817
|
if (!packageJson.catalog) {
|
|
28760
28818
|
packageJson.catalog = {};
|
|
28761
28819
|
}
|
|
28762
|
-
|
|
28820
|
+
if (shadcnBase === "base-ui") {
|
|
28821
|
+
packageJson.catalog["@base-ui/react"] = "^1.2.0";
|
|
28822
|
+
} else {
|
|
28823
|
+
packageJson.catalog["radix-ui"] = "^1.4.3";
|
|
28824
|
+
}
|
|
28763
28825
|
packageJson.catalog["class-variance-authority"] = "^0.7.1";
|
|
28764
28826
|
packageJson.catalog.clsx = "^2.1.1";
|
|
28765
28827
|
packageJson.catalog["tailwind-merge"] = "^3.4.0";
|
|
28766
|
-
packageJson.catalog["lucide-react"] = "^0.562.0";
|
|
28767
28828
|
packageJson.catalog["tw-animate-css"] = "^1.2.9";
|
|
28829
|
+
if (iconLibrary === "iconoir") {
|
|
28830
|
+
packageJson.catalog["iconoir-react"] = "^7.11.0";
|
|
28831
|
+
} else if (iconLibrary === "phosphor") {
|
|
28832
|
+
packageJson.catalog["@phosphor-icons/react"] = "^2.1.10";
|
|
28833
|
+
} else {
|
|
28834
|
+
packageJson.catalog["lucide-react"] = "^0.562.0";
|
|
28835
|
+
}
|
|
28768
28836
|
await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
28769
28837
|
const globalsCssPath = join(projectPath, "src/app/globals.css");
|
|
28770
28838
|
let globalsCss = "";
|
|
@@ -28791,9 +28859,11 @@ ${shadcnCss}`;
|
|
|
28791
28859
|
async function setupShadcnMonorepo(projectPath, context) {
|
|
28792
28860
|
const packageName = context.packageName || context.projectName.toLowerCase().replace(/\s+/g, "-");
|
|
28793
28861
|
const uiPackageName = `@${packageName}/ui`;
|
|
28794
|
-
const style = context.shadcnStyle || "
|
|
28862
|
+
const style = context.shadcnStyle || "radix-maia";
|
|
28795
28863
|
const baseColor = context.shadcnBaseColor || "zinc";
|
|
28796
28864
|
const radius = context.shadcnRadius || "0.625rem";
|
|
28865
|
+
const iconLibrary = context.shadcnIconLibrary || "iconoir";
|
|
28866
|
+
const shadcnBase = inferShadcnBase(context.shadcnStyle);
|
|
28797
28867
|
await ensureDirectory(join(projectPath, "packages/ui/src/components"));
|
|
28798
28868
|
await ensureDirectory(join(projectPath, "packages/ui/src/lib"));
|
|
28799
28869
|
await ensureDirectory(join(projectPath, "packages/ui/src/hooks"));
|
|
@@ -28815,11 +28885,11 @@ async function setupShadcnMonorepo(projectPath, context) {
|
|
|
28815
28885
|
"./components/ui/*": "./src/components/ui/*/index.ts"
|
|
28816
28886
|
},
|
|
28817
28887
|
dependencies: {
|
|
28818
|
-
"@
|
|
28888
|
+
...shadcnBase === "base-ui" ? { "@base-ui/react": "catalog:" } : { "radix-ui": "catalog:" },
|
|
28819
28889
|
"class-variance-authority": "catalog:",
|
|
28820
28890
|
clsx: "catalog:",
|
|
28821
28891
|
"tailwind-merge": "catalog:",
|
|
28822
|
-
"lucide-react": "catalog:",
|
|
28892
|
+
...iconLibrary === "iconoir" ? { "iconoir-react": "catalog:" } : iconLibrary === "phosphor" ? { "@phosphor-icons/react": "catalog:" } : { "lucide-react": "catalog:" },
|
|
28823
28893
|
tailwindcss: "catalog:",
|
|
28824
28894
|
"@tailwindcss/postcss": "catalog:"
|
|
28825
28895
|
},
|
|
@@ -28841,7 +28911,7 @@ async function setupShadcnMonorepo(projectPath, context) {
|
|
|
28841
28911
|
baseColor,
|
|
28842
28912
|
cssVariables: true
|
|
28843
28913
|
},
|
|
28844
|
-
iconLibrary
|
|
28914
|
+
iconLibrary,
|
|
28845
28915
|
aliases: {
|
|
28846
28916
|
components: "./src/components",
|
|
28847
28917
|
ui: "./src/components/ui",
|
|
@@ -28850,6 +28920,9 @@ async function setupShadcnMonorepo(projectPath, context) {
|
|
|
28850
28920
|
lib: "@/lib"
|
|
28851
28921
|
}
|
|
28852
28922
|
};
|
|
28923
|
+
if (context.shadcnRtl) {
|
|
28924
|
+
uiComponentsJson.rtl = true;
|
|
28925
|
+
}
|
|
28853
28926
|
await writeFile(join(projectPath, "packages/ui/components.json"), JSON.stringify(uiComponentsJson, null, 2));
|
|
28854
28927
|
const uiUtilsContent = `import { clsx, type ClassValue } from 'clsx';
|
|
28855
28928
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -28939,7 +29012,7 @@ export default config;
|
|
|
28939
29012
|
baseColor,
|
|
28940
29013
|
cssVariables: true
|
|
28941
29014
|
},
|
|
28942
|
-
iconLibrary
|
|
29015
|
+
iconLibrary,
|
|
28943
29016
|
aliases: {
|
|
28944
29017
|
components: "@/components",
|
|
28945
29018
|
hooks: "@/hooks",
|
|
@@ -28948,6 +29021,9 @@ export default config;
|
|
|
28948
29021
|
ui: "@workspace/ui/components/ui"
|
|
28949
29022
|
}
|
|
28950
29023
|
};
|
|
29024
|
+
if (context.shadcnRtl) {
|
|
29025
|
+
appComponentsJson.rtl = true;
|
|
29026
|
+
}
|
|
28951
29027
|
await writeFile(join(appPath, "components.json"), JSON.stringify(appComponentsJson, null, 2));
|
|
28952
29028
|
const layoutPath = join(appPath, "src/app/layout.tsx");
|
|
28953
29029
|
if (await fileExists(layoutPath)) {
|
|
@@ -29043,16 +29119,26 @@ export default nextConfig;
|
|
|
29043
29119
|
rootPackageJson.catalog = {};
|
|
29044
29120
|
}
|
|
29045
29121
|
const shadcnDependencies = {
|
|
29046
|
-
"@radix-ui/react-slot": "^1.2.4",
|
|
29047
29122
|
"class-variance-authority": "^0.7.1",
|
|
29048
29123
|
clsx: "^2.1.1",
|
|
29049
29124
|
"tailwind-merge": "^3.4.0",
|
|
29050
|
-
"lucide-react": "^0.562.0",
|
|
29051
29125
|
"tw-animate-css": "^1.2.9",
|
|
29052
29126
|
"@types/react": "^19.2.7",
|
|
29053
29127
|
"@types/react-dom": "^19.2.3",
|
|
29054
29128
|
typescript: "^5.9.3"
|
|
29055
29129
|
};
|
|
29130
|
+
if (shadcnBase === "base-ui") {
|
|
29131
|
+
shadcnDependencies["@base-ui/react"] = "^1.2.0";
|
|
29132
|
+
} else {
|
|
29133
|
+
shadcnDependencies["radix-ui"] = "^1.4.3";
|
|
29134
|
+
}
|
|
29135
|
+
if (iconLibrary === "iconoir") {
|
|
29136
|
+
shadcnDependencies["iconoir-react"] = "^7.11.0";
|
|
29137
|
+
} else if (iconLibrary === "phosphor") {
|
|
29138
|
+
shadcnDependencies["@phosphor-icons/react"] = "^2.1.10";
|
|
29139
|
+
} else {
|
|
29140
|
+
shadcnDependencies["lucide-react"] = "^0.562.0";
|
|
29141
|
+
}
|
|
29056
29142
|
Object.assign(rootPackageJson.catalog, shadcnDependencies);
|
|
29057
29143
|
await writeFile(rootPackageJsonPath, JSON.stringify(rootPackageJson, null, 2));
|
|
29058
29144
|
await createShadcnDocs(join(projectPath, "packages/ui"), true, context);
|
|
@@ -29123,7 +29209,7 @@ async function setupTooling(projectPath, context) {
|
|
|
29123
29209
|
extends: "../../tooling/typescript/base.json",
|
|
29124
29210
|
compilerOptions: {
|
|
29125
29211
|
target: "ESNext",
|
|
29126
|
-
lib: ["ESNext"],
|
|
29212
|
+
lib: ["ESNext", "DOM", "DOM.Iterable"],
|
|
29127
29213
|
module: "ESNext",
|
|
29128
29214
|
moduleResolution: "bundler",
|
|
29129
29215
|
declaration: true,
|
|
@@ -29447,11 +29533,15 @@ async function buildEnterprisePreset(projectPath, context) {
|
|
|
29447
29533
|
autoprefixer: "^10.4.23",
|
|
29448
29534
|
postcss: "^8.5.6",
|
|
29449
29535
|
"@tailwindcss/postcss": "^4.1.18",
|
|
29536
|
+
"radix-ui": "^1.4.3",
|
|
29450
29537
|
"@radix-ui/react-slot": "^1.2.4",
|
|
29538
|
+
"@base-ui/react": "^1.2.0",
|
|
29539
|
+
shadcn: "^3.8.5",
|
|
29451
29540
|
"class-variance-authority": "^0.7.1",
|
|
29452
29541
|
clsx: "^2.1.1",
|
|
29453
29542
|
"tailwind-merge": "^3.4.0",
|
|
29454
29543
|
"iconoir-react": "^7.11.0",
|
|
29544
|
+
"@phosphor-icons/react": "^2.1.10",
|
|
29455
29545
|
"lucide-react": "^0.562.0",
|
|
29456
29546
|
"tw-animate-css": "^1.2.9",
|
|
29457
29547
|
"@biomejs/biome": "^2.3.10",
|
|
@@ -29624,9 +29714,11 @@ async function buildEnterprisePreset(projectPath, context) {
|
|
|
29624
29714
|
const enterpriseContext = {
|
|
29625
29715
|
...context,
|
|
29626
29716
|
uiLibrary: "shadcn",
|
|
29627
|
-
shadcnStyle: context.shadcnStyle || "
|
|
29717
|
+
shadcnStyle: context.shadcnStyle || "radix-maia",
|
|
29628
29718
|
shadcnBaseColor: context.shadcnBaseColor || "zinc",
|
|
29629
|
-
shadcnRadius: context.shadcnRadius || "0.625rem"
|
|
29719
|
+
shadcnRadius: context.shadcnRadius || "0.625rem",
|
|
29720
|
+
shadcnIconLibrary: context.shadcnIconLibrary || "iconoir",
|
|
29721
|
+
shadcnRtl: context.shadcnRtl
|
|
29630
29722
|
};
|
|
29631
29723
|
await setupShadcnMonorepo(projectPath, enterpriseContext);
|
|
29632
29724
|
}
|
|
@@ -29787,11 +29879,15 @@ async function buildFullPreset(projectPath, context) {
|
|
|
29787
29879
|
autoprefixer: "^10.4.23",
|
|
29788
29880
|
postcss: "^8.5.6",
|
|
29789
29881
|
"@tailwindcss/postcss": "^4.1.18",
|
|
29882
|
+
"radix-ui": "^1.4.3",
|
|
29790
29883
|
"@radix-ui/react-slot": "^1.2.4",
|
|
29884
|
+
"@base-ui/react": "^1.2.0",
|
|
29885
|
+
shadcn: "^3.8.5",
|
|
29791
29886
|
"class-variance-authority": "^0.7.1",
|
|
29792
29887
|
clsx: "^2.1.1",
|
|
29793
29888
|
"tailwind-merge": "^3.4.0",
|
|
29794
29889
|
"iconoir-react": "^7.11.0",
|
|
29890
|
+
"@phosphor-icons/react": "^2.1.10",
|
|
29795
29891
|
"lucide-react": "^0.562.0",
|
|
29796
29892
|
"tw-animate-css": "^1.2.9",
|
|
29797
29893
|
...context.codeQuality === "biome" ? { "@biomejs/biome": "^2.3.10" } : {},
|
|
@@ -30585,1046 +30681,1023 @@ networks:
|
|
|
30585
30681
|
const fullContext = {
|
|
30586
30682
|
...context,
|
|
30587
30683
|
uiLibrary: "shadcn",
|
|
30588
|
-
shadcnStyle: context.shadcnStyle || "
|
|
30684
|
+
shadcnStyle: context.shadcnStyle || "radix-maia",
|
|
30589
30685
|
shadcnBaseColor: context.shadcnBaseColor || "zinc",
|
|
30590
|
-
shadcnRadius: context.shadcnRadius || "0.625rem"
|
|
30686
|
+
shadcnRadius: context.shadcnRadius || "0.625rem",
|
|
30687
|
+
shadcnIconLibrary: context.shadcnIconLibrary || "iconoir",
|
|
30688
|
+
shadcnRtl: context.shadcnRtl
|
|
30591
30689
|
};
|
|
30592
30690
|
await setupShadcnMonorepo(projectPath, fullContext);
|
|
30593
30691
|
}
|
|
30594
30692
|
await setupTooling(projectPath, context);
|
|
30595
30693
|
await setupVSCodeDebug(projectPath, context, "full");
|
|
30596
30694
|
}
|
|
30597
|
-
// ../templates/src/builders/
|
|
30695
|
+
// ../templates/src/builders/full-v2.ts
|
|
30598
30696
|
init_src();
|
|
30599
30697
|
init_dist();
|
|
30600
|
-
|
|
30601
|
-
const indexContent = `console.log('Hello from ${context.projectName}! \uD83C\uDF5E');
|
|
30602
|
-
|
|
30603
|
-
// Your code here
|
|
30604
|
-
const greet = (name: string): void => {
|
|
30605
|
-
console.log(\`Welcome, \${name}!\`);
|
|
30606
|
-
};
|
|
30698
|
+
init_package_json();
|
|
30607
30699
|
|
|
30608
|
-
|
|
30609
|
-
|
|
30610
|
-
|
|
30611
|
-
|
|
30700
|
+
// ../templates/src/shared/ui-package.ts
|
|
30701
|
+
init_src();
|
|
30702
|
+
init_dist();
|
|
30703
|
+
init_package_json();
|
|
30704
|
+
async function buildUiPackage(packagesPath, options) {
|
|
30705
|
+
const uiPath = join(packagesPath, "ui");
|
|
30706
|
+
await ensureDirectory(join(uiPath, "src/components"));
|
|
30707
|
+
await ensureDirectory(join(uiPath, "src/hooks"));
|
|
30708
|
+
await ensureDirectory(join(uiPath, "src/lib"));
|
|
30709
|
+
await ensureDirectory(join(uiPath, "src/styles"));
|
|
30710
|
+
const style = options.shadcnStyle || "radix-maia";
|
|
30711
|
+
const useModernStyle = isModernShadcnStyle(style);
|
|
30712
|
+
const shadcnBase = options.shadcnBase || inferShadcnBase(style);
|
|
30713
|
+
const iconLibrary = options.shadcnIconLibrary || "iconoir";
|
|
30714
|
+
await writeUiPackageJson(uiPath, options.scopeName, {
|
|
30715
|
+
shadcnBase,
|
|
30716
|
+
shadcnIconLibrary: iconLibrary
|
|
30717
|
+
});
|
|
30718
|
+
await writeFile(join(uiPath, "tsconfig.json"), JSON.stringify({
|
|
30719
|
+
extends: "../../tooling/typescript/library.json",
|
|
30612
30720
|
compilerOptions: {
|
|
30613
|
-
lib: ["ESNext"],
|
|
30614
|
-
target: "ESNext",
|
|
30615
|
-
module: "ESNext",
|
|
30616
|
-
moduleDetection: "force",
|
|
30617
30721
|
jsx: "react-jsx",
|
|
30618
|
-
|
|
30619
|
-
|
|
30620
|
-
|
|
30621
|
-
|
|
30622
|
-
|
|
30623
|
-
|
|
30624
|
-
|
|
30625
|
-
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
|
|
30722
|
+
rootDir: "./src",
|
|
30723
|
+
outDir: "./dist",
|
|
30724
|
+
baseUrl: ".",
|
|
30725
|
+
paths: {
|
|
30726
|
+
"@/*": ["./src/*"]
|
|
30727
|
+
}
|
|
30728
|
+
},
|
|
30729
|
+
include: ["src/**/*"],
|
|
30730
|
+
exclude: ["node_modules"]
|
|
30731
|
+
}, null, 2));
|
|
30732
|
+
const componentsJson = {
|
|
30733
|
+
$schema: "https://ui.shadcn.com/schema.json",
|
|
30734
|
+
style,
|
|
30735
|
+
rsc: true,
|
|
30736
|
+
tsx: true,
|
|
30737
|
+
tailwind: {
|
|
30738
|
+
config: "",
|
|
30739
|
+
css: "./src/styles/globals.css",
|
|
30740
|
+
baseColor: options.shadcnBaseColor || "zinc",
|
|
30741
|
+
cssVariables: true,
|
|
30742
|
+
prefix: ""
|
|
30743
|
+
},
|
|
30744
|
+
iconLibrary,
|
|
30745
|
+
aliases: {
|
|
30746
|
+
components: "@/components",
|
|
30747
|
+
utils: "@/lib/utils",
|
|
30748
|
+
ui: "@/components/ui",
|
|
30749
|
+
lib: "@/lib",
|
|
30750
|
+
hooks: "@/hooks"
|
|
30630
30751
|
}
|
|
30631
30752
|
};
|
|
30632
|
-
|
|
30633
|
-
|
|
30634
|
-
|
|
30635
|
-
|
|
30636
|
-
await setupUltracite(projectPath, context);
|
|
30637
|
-
} else {
|
|
30638
|
-
await setupBiome(projectPath, context);
|
|
30753
|
+
if (useModernStyle) {
|
|
30754
|
+
componentsJson.menuColor = options.shadcnMenuColor || "default";
|
|
30755
|
+
componentsJson.menuAccent = options.shadcnMenuAccent || "subtle";
|
|
30756
|
+
componentsJson.registries = {};
|
|
30639
30757
|
}
|
|
30640
|
-
|
|
30641
|
-
|
|
30642
|
-
}
|
|
30643
|
-
// ../templates/src/builders/monorepo-bun.ts
|
|
30644
|
-
init_src();
|
|
30645
|
-
init_dist();
|
|
30646
|
-
var databaseSetupMap6 = {
|
|
30647
|
-
"postgres-drizzle": setupPostgresDrizzle,
|
|
30648
|
-
"postgres-prisma": setupPostgresPrisma,
|
|
30649
|
-
"mysql-drizzle": setupMySQLDrizzle,
|
|
30650
|
-
"mysql-prisma": setupMySQLPrisma,
|
|
30651
|
-
supabase: setupSupabaseOnly,
|
|
30652
|
-
"supabase-drizzle": setupSupabaseDrizzle,
|
|
30653
|
-
"supabase-prisma": setupSupabasePrisma,
|
|
30654
|
-
"sqlite-drizzle": setupSQLiteDrizzle,
|
|
30655
|
-
"sqlite-prisma": setupSQLitePrisma,
|
|
30656
|
-
none: async () => {}
|
|
30657
|
-
};
|
|
30658
|
-
async function buildMonorepoBunPreset(projectPath, context) {
|
|
30659
|
-
await ensureDirectory(join(projectPath, "apps/web"));
|
|
30660
|
-
await ensureDirectory(join(projectPath, "apps/api"));
|
|
30661
|
-
await ensureDirectory(join(projectPath, "packages/types"));
|
|
30662
|
-
await ensureDirectory(join(projectPath, "packages/utils"));
|
|
30663
|
-
if (context.database && context.database !== "none") {
|
|
30664
|
-
await ensureDirectory(join(projectPath, "packages/db"));
|
|
30758
|
+
if (options.shadcnRtl) {
|
|
30759
|
+
componentsJson.rtl = true;
|
|
30665
30760
|
}
|
|
30666
|
-
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
|
|
30671
|
-
|
|
30672
|
-
|
|
30673
|
-
|
|
30674
|
-
|
|
30675
|
-
|
|
30676
|
-
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
|
|
30683
|
-
|
|
30684
|
-
|
|
30685
|
-
|
|
30686
|
-
|
|
30687
|
-
|
|
30688
|
-
|
|
30689
|
-
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
"drizzle-kit": "^0.31.8",
|
|
30693
|
-
postgres: "^3.4.7",
|
|
30694
|
-
"@supabase/supabase-js": "^2.88.0",
|
|
30695
|
-
"@prisma/client": "^7.2.0",
|
|
30696
|
-
prisma: "^7.2.0",
|
|
30697
|
-
mysql2: "^3.16.0",
|
|
30698
|
-
"better-auth": "^1.4.7",
|
|
30699
|
-
"next-auth": "^4.24.13",
|
|
30700
|
-
"@auth/drizzle-adapter": "^1.11.1",
|
|
30701
|
-
tailwindcss: "^4.1.18",
|
|
30702
|
-
autoprefixer: "^10.4.23",
|
|
30703
|
-
postcss: "^8.5.6",
|
|
30704
|
-
typescript: "^5.9.3",
|
|
30705
|
-
"@types/react": "^19.2.7",
|
|
30706
|
-
"@types/react-dom": "^19.2.3",
|
|
30707
|
-
"@types/node": "^25.0.3"
|
|
30708
|
-
}
|
|
30709
|
-
};
|
|
30710
|
-
await writeFile(join(projectPath, "package.json"), JSON.stringify(rootPackageJson, null, 2));
|
|
30711
|
-
const apiPath = join(projectPath, "apps/api");
|
|
30712
|
-
await setupBunServeNative(apiPath, context, true);
|
|
30713
|
-
const apiPackageJson = {
|
|
30714
|
-
name: `@${context.packageName}/api`,
|
|
30715
|
-
version: "0.0.0",
|
|
30716
|
-
private: true,
|
|
30717
|
-
scripts: {
|
|
30718
|
-
dev: "bun run --hot src/index.ts",
|
|
30719
|
-
start: "bun run src/index.ts",
|
|
30720
|
-
debug: "bun --inspect src/index.ts",
|
|
30721
|
-
"debug:brk": "bun --inspect-brk src/index.ts",
|
|
30722
|
-
"debug:wait": "bun --inspect-wait src/index.ts"
|
|
30723
|
-
},
|
|
30724
|
-
dependencies: {
|
|
30725
|
-
[`@${context.packageName}/types`]: "workspace:*"
|
|
30726
|
-
},
|
|
30727
|
-
devDependencies: {
|
|
30728
|
-
"@types/bun": "latest",
|
|
30729
|
-
typescript: "catalog:"
|
|
30730
|
-
}
|
|
30731
|
-
};
|
|
30732
|
-
await writeFile(join(apiPath, "package.json"), JSON.stringify(apiPackageJson, null, 2));
|
|
30733
|
-
const webPath = join(projectPath, "apps/web");
|
|
30734
|
-
await setupBunFullstack(webPath, context);
|
|
30735
|
-
const webPackageJson = {
|
|
30736
|
-
name: `@${context.packageName}/web`,
|
|
30737
|
-
version: "0.0.0",
|
|
30738
|
-
private: true,
|
|
30739
|
-
scripts: {
|
|
30740
|
-
dev: "bun run --hot src/index.ts",
|
|
30741
|
-
build: "bun build src/index.ts --outdir ./dist --target bun",
|
|
30742
|
-
start: "bun run dist/index.js",
|
|
30743
|
-
debug: "bun --inspect src/index.ts",
|
|
30744
|
-
"debug:brk": "bun --inspect-brk src/index.ts",
|
|
30745
|
-
"debug:wait": "bun --inspect-wait src/index.ts"
|
|
30746
|
-
},
|
|
30747
|
-
dependencies: {
|
|
30748
|
-
react: "catalog:",
|
|
30749
|
-
"react-dom": "catalog:",
|
|
30750
|
-
[`@${context.packageName}/types`]: "workspace:*"
|
|
30751
|
-
},
|
|
30752
|
-
devDependencies: {
|
|
30753
|
-
"@types/react": "catalog:",
|
|
30754
|
-
"@types/react-dom": "catalog:",
|
|
30755
|
-
"@types/bun": "latest",
|
|
30756
|
-
typescript: "catalog:"
|
|
30757
|
-
}
|
|
30758
|
-
};
|
|
30759
|
-
await writeFile(join(webPath, "package.json"), JSON.stringify(webPackageJson, null, 2));
|
|
30760
|
-
if (context.database && context.database !== "none") {
|
|
30761
|
-
await databaseSetupMap6[context.database](join(projectPath, "packages/db"), context, true);
|
|
30762
|
-
}
|
|
30763
|
-
if (context.redis) {
|
|
30764
|
-
await setupRedis(join(projectPath, "packages"), context, true);
|
|
30765
|
-
}
|
|
30766
|
-
if (context.auth === "better-auth") {
|
|
30767
|
-
await setupBetterAuth(join(projectPath, "packages"), context, true);
|
|
30768
|
-
} else if (context.auth === "nextauth") {
|
|
30769
|
-
await setupNextAuth(join(projectPath, "packages"), context, true);
|
|
30770
|
-
}
|
|
30771
|
-
if (context.useBunSecrets) {
|
|
30772
|
-
await setupBunSecrets(join(projectPath, "packages"), context, true);
|
|
30773
|
-
}
|
|
30774
|
-
if (context.codeQuality === "biome") {
|
|
30775
|
-
await setupBiome(projectPath, context);
|
|
30761
|
+
await writeFile(join(uiPath, "components.json"), JSON.stringify(componentsJson, null, 2));
|
|
30762
|
+
await writeFile(join(uiPath, "postcss.config.mjs"), `export default {
|
|
30763
|
+
plugins: {
|
|
30764
|
+
'@tailwindcss/postcss': {},
|
|
30765
|
+
},
|
|
30766
|
+
};
|
|
30767
|
+
`);
|
|
30768
|
+
const appsToScan = options.appsToScan || ["web", "platform"];
|
|
30769
|
+
const appSourcePaths = appsToScan.map((app) => `@source "../../../apps/${app}/src/**/*.{ts,tsx}";`);
|
|
30770
|
+
const baseColor = options.shadcnBaseColor || "zinc";
|
|
30771
|
+
const customRadius = options.shadcnRadius || "0.625rem";
|
|
30772
|
+
let globalsCss;
|
|
30773
|
+
if (useModernStyle) {
|
|
30774
|
+
const modernThemeCSS = generateModernThemeCSS(baseColor, customRadius);
|
|
30775
|
+
const shadcnImport = shadcnBase === "base-ui" ? "" : `
|
|
30776
|
+
@import "shadcn/tailwind.css";`;
|
|
30777
|
+
globalsCss = `@import "tailwindcss";
|
|
30778
|
+
@import "tw-animate-css";${shadcnImport}
|
|
30779
|
+
${appSourcePaths.join(`
|
|
30780
|
+
`)}
|
|
30781
|
+
@source "../**/*.{ts,tsx}";
|
|
30782
|
+
|
|
30783
|
+
@custom-variant dark (&:is(.dark *));
|
|
30784
|
+
|
|
30785
|
+
${modernThemeCSS}
|
|
30786
|
+
`;
|
|
30776
30787
|
} else {
|
|
30777
|
-
|
|
30778
|
-
|
|
30779
|
-
|
|
30780
|
-
|
|
30781
|
-
|
|
30782
|
-
|
|
30783
|
-
|
|
30788
|
+
const theme = themes[baseColor] || themes.zinc;
|
|
30789
|
+
const themeCSS = generateThemeCSS(theme, customRadius);
|
|
30790
|
+
globalsCss = `@import "tailwindcss";
|
|
30791
|
+
${appSourcePaths.join(`
|
|
30792
|
+
`)}
|
|
30793
|
+
@source "../**/*.{ts,tsx}";
|
|
30794
|
+
|
|
30795
|
+
${themeCSS}
|
|
30796
|
+
`;
|
|
30784
30797
|
}
|
|
30785
|
-
await
|
|
30786
|
-
await
|
|
30787
|
-
|
|
30788
|
-
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
|
|
30793
|
-
|
|
30794
|
-
|
|
30795
|
-
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30800
|
-
|
|
30801
|
-
|
|
30802
|
-
|
|
30798
|
+
await writeFile(join(uiPath, "src/styles/globals.css"), globalsCss);
|
|
30799
|
+
await writeFile(join(uiPath, "src/lib/utils.ts"), `import { clsx, type ClassValue } from 'clsx';
|
|
30800
|
+
import { twMerge } from 'tailwind-merge';
|
|
30801
|
+
|
|
30802
|
+
export function cn(...inputs: ClassValue[]) {
|
|
30803
|
+
return twMerge(clsx(inputs));
|
|
30804
|
+
}
|
|
30805
|
+
`);
|
|
30806
|
+
await writeFile(join(uiPath, "src/components/index.ts"), `/**
|
|
30807
|
+
* UI Components
|
|
30808
|
+
*
|
|
30809
|
+
* Add shadcn/ui components using:
|
|
30810
|
+
* bunx shadcn@latest add button card dialog ...
|
|
30811
|
+
*
|
|
30812
|
+
* Then export them here for use across apps.
|
|
30813
|
+
*/
|
|
30814
|
+
|
|
30815
|
+
// Export components as they are added
|
|
30816
|
+
// Example: export { Button } from './ui/button';
|
|
30817
|
+
|
|
30818
|
+
// Empty export to make this a valid ES module
|
|
30819
|
+
export {};
|
|
30820
|
+
`);
|
|
30821
|
+
await writeFile(join(uiPath, "src/hooks/index.ts"), `/**
|
|
30822
|
+
* Shared Hooks
|
|
30823
|
+
*
|
|
30824
|
+
* Custom React hooks shared across apps.
|
|
30825
|
+
*/
|
|
30826
|
+
|
|
30827
|
+
// Export hooks as they are added
|
|
30828
|
+
// Example: export { useMediaQuery } from './use-media-query';
|
|
30829
|
+
|
|
30830
|
+
// Empty export to make this a valid ES module
|
|
30831
|
+
export {};
|
|
30832
|
+
`);
|
|
30833
|
+
await writeFile(join(uiPath, "src/index.ts"), `/**
|
|
30834
|
+
* @${options.scopeName}/ui - Shared UI Components
|
|
30835
|
+
*
|
|
30836
|
+
* This package provides:
|
|
30837
|
+
* - shadcn/ui components (add with: bunx shadcn@latest add <component>)
|
|
30838
|
+
* - Tailwind CSS v4 configuration
|
|
30839
|
+
* - Shared hooks and utilities
|
|
30840
|
+
*
|
|
30841
|
+
* Usage in apps:
|
|
30842
|
+
* - Import globals.css: import '@${options.scopeName}/ui/globals.css';
|
|
30843
|
+
* - Import components: import { Button } from '@${options.scopeName}/ui/components';
|
|
30844
|
+
* - Import utils: import { cn } from '@${options.scopeName}/ui/lib/utils';
|
|
30845
|
+
*/
|
|
30846
|
+
|
|
30847
|
+
export * from './lib/utils';
|
|
30848
|
+
export * from './hooks';
|
|
30849
|
+
export * from './components';
|
|
30850
|
+
`);
|
|
30851
|
+
}
|
|
30852
|
+
async function buildTypesPackage(packagesPath, scopeName) {
|
|
30853
|
+
const typesPath = join(packagesPath, "types");
|
|
30854
|
+
await ensureDirectory(join(typesPath, "src"));
|
|
30855
|
+
const { writeTypesPackageJson: writeTypesPackageJson2 } = await Promise.resolve().then(() => (init_package_json(), exports_package_json));
|
|
30856
|
+
await writeTypesPackageJson2(typesPath, scopeName);
|
|
30857
|
+
await writeFile(join(typesPath, "tsconfig.json"), JSON.stringify({
|
|
30858
|
+
extends: "../../tooling/typescript/library.json",
|
|
30859
|
+
compilerOptions: {
|
|
30860
|
+
rootDir: "./src",
|
|
30861
|
+
outDir: "./dist"
|
|
30862
|
+
},
|
|
30863
|
+
include: ["src/**/*"],
|
|
30864
|
+
exclude: ["node_modules"]
|
|
30865
|
+
}, null, 2));
|
|
30866
|
+
await writeFile(join(typesPath, "src/index.ts"), `/**
|
|
30867
|
+
* @${scopeName}/types - Shared TypeScript Types
|
|
30868
|
+
*
|
|
30869
|
+
* Define types that are shared across multiple packages/apps here.
|
|
30870
|
+
*/
|
|
30871
|
+
|
|
30872
|
+
// Example types - replace with your actual types
|
|
30873
|
+
|
|
30803
30874
|
export interface User {
|
|
30804
|
-
id:
|
|
30805
|
-
name: string;
|
|
30875
|
+
id: string;
|
|
30806
30876
|
email: string;
|
|
30877
|
+
name?: string;
|
|
30807
30878
|
createdAt: Date;
|
|
30879
|
+
updatedAt: Date;
|
|
30808
30880
|
}
|
|
30809
30881
|
|
|
30810
|
-
export interface ApiResponse<T
|
|
30882
|
+
export interface ApiResponse<T> {
|
|
30811
30883
|
success: boolean;
|
|
30812
30884
|
data?: T;
|
|
30813
|
-
error?:
|
|
30814
|
-
|
|
30815
|
-
|
|
30816
|
-
`;
|
|
30817
|
-
await writeFile(join(projectPath, "packages/types/index.ts"), typesIndex);
|
|
30818
|
-
const utilsPackageJson = {
|
|
30819
|
-
name: `@${context.packageName}/utils`,
|
|
30820
|
-
version: "0.0.0",
|
|
30821
|
-
private: true,
|
|
30822
|
-
main: "./index.ts",
|
|
30823
|
-
types: "./index.ts",
|
|
30824
|
-
scripts: {},
|
|
30825
|
-
dependencies: {},
|
|
30826
|
-
devDependencies: {
|
|
30827
|
-
typescript: "catalog:"
|
|
30828
|
-
}
|
|
30885
|
+
error?: {
|
|
30886
|
+
code: string;
|
|
30887
|
+
message: string;
|
|
30829
30888
|
};
|
|
30830
|
-
await ensureDirectory(join(projectPath, "packages/utils"));
|
|
30831
|
-
await writeFile(join(projectPath, "packages/utils/package.json"), JSON.stringify(utilsPackageJson, null, 2));
|
|
30832
|
-
const utilsIndex = `// Shared utilities across the monorepo
|
|
30833
|
-
export function formatDate(date: Date): string {
|
|
30834
|
-
return date.toISOString();
|
|
30835
30889
|
}
|
|
30836
30890
|
|
|
30837
|
-
export
|
|
30838
|
-
|
|
30891
|
+
export interface PaginatedResponse<T> extends ApiResponse<T[]> {
|
|
30892
|
+
pagination: {
|
|
30893
|
+
page: number;
|
|
30894
|
+
perPage: number;
|
|
30895
|
+
total: number;
|
|
30896
|
+
totalPages: number;
|
|
30897
|
+
};
|
|
30839
30898
|
}
|
|
30840
|
-
|
|
30841
|
-
|
|
30842
|
-
|
|
30843
|
-
const
|
|
30844
|
-
|
|
30845
|
-
|
|
30846
|
-
|
|
30899
|
+
`);
|
|
30900
|
+
}
|
|
30901
|
+
async function buildUtilsPackage(packagesPath, scopeName) {
|
|
30902
|
+
const utilsPath = join(packagesPath, "utils");
|
|
30903
|
+
await ensureDirectory(join(utilsPath, "src"));
|
|
30904
|
+
const { writeUtilsPackageJson: writeUtilsPackageJson2 } = await Promise.resolve().then(() => (init_package_json(), exports_package_json));
|
|
30905
|
+
await writeUtilsPackageJson2(utilsPath, scopeName);
|
|
30906
|
+
await writeFile(join(utilsPath, "tsconfig.json"), JSON.stringify({
|
|
30907
|
+
extends: "../../tooling/typescript/library.json",
|
|
30908
|
+
compilerOptions: {
|
|
30909
|
+
rootDir: "./src",
|
|
30910
|
+
outDir: "./dist"
|
|
30911
|
+
},
|
|
30912
|
+
include: ["src/**/*"],
|
|
30847
30913
|
exclude: ["node_modules"]
|
|
30914
|
+
}, null, 2));
|
|
30915
|
+
await writeFile(join(utilsPath, "src/index.ts"), `/**
|
|
30916
|
+
* @${scopeName}/utils - Shared Utilities
|
|
30917
|
+
*
|
|
30918
|
+
* Utility functions shared across packages and apps.
|
|
30919
|
+
*/
|
|
30920
|
+
|
|
30921
|
+
/**
|
|
30922
|
+
* Format a date to a human-readable string
|
|
30923
|
+
*/
|
|
30924
|
+
export function formatDate(date: Date | string, locale = 'en-US'): string {
|
|
30925
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
30926
|
+
return d.toLocaleDateString(locale, {
|
|
30927
|
+
year: 'numeric',
|
|
30928
|
+
month: 'long',
|
|
30929
|
+
day: 'numeric',
|
|
30930
|
+
});
|
|
30931
|
+
}
|
|
30932
|
+
|
|
30933
|
+
/**
|
|
30934
|
+
* Format a number as currency
|
|
30935
|
+
*/
|
|
30936
|
+
export function formatCurrency(
|
|
30937
|
+
amount: number,
|
|
30938
|
+
currency = 'USD',
|
|
30939
|
+
locale = 'en-US'
|
|
30940
|
+
): string {
|
|
30941
|
+
return new Intl.NumberFormat(locale, {
|
|
30942
|
+
style: 'currency',
|
|
30943
|
+
currency,
|
|
30944
|
+
}).format(amount);
|
|
30945
|
+
}
|
|
30946
|
+
|
|
30947
|
+
/**
|
|
30948
|
+
* Sleep for a specified number of milliseconds
|
|
30949
|
+
*/
|
|
30950
|
+
export function sleep(ms: number): Promise<void> {
|
|
30951
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30952
|
+
}
|
|
30953
|
+
|
|
30954
|
+
/**
|
|
30955
|
+
* Safely parse JSON with a fallback value
|
|
30956
|
+
*/
|
|
30957
|
+
export function safeJsonParse<T>(json: string, fallback: T): T {
|
|
30958
|
+
try {
|
|
30959
|
+
return JSON.parse(json) as T;
|
|
30960
|
+
} catch {
|
|
30961
|
+
return fallback;
|
|
30962
|
+
}
|
|
30963
|
+
}
|
|
30964
|
+
|
|
30965
|
+
/**
|
|
30966
|
+
* Generate a random ID
|
|
30967
|
+
*/
|
|
30968
|
+
export function generateId(length = 12): string {
|
|
30969
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
30970
|
+
let result = '';
|
|
30971
|
+
for (let i = 0; i < length; i++) {
|
|
30972
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
30973
|
+
}
|
|
30974
|
+
return result;
|
|
30975
|
+
}
|
|
30976
|
+
|
|
30977
|
+
/**
|
|
30978
|
+
* Debounce a function
|
|
30979
|
+
*/
|
|
30980
|
+
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
30981
|
+
func: T,
|
|
30982
|
+
wait: number
|
|
30983
|
+
): (...args: Parameters<T>) => void {
|
|
30984
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
30985
|
+
return (...args: Parameters<T>) => {
|
|
30986
|
+
if (timeout) clearTimeout(timeout);
|
|
30987
|
+
timeout = setTimeout(() => func(...args), wait);
|
|
30848
30988
|
};
|
|
30849
|
-
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
|
|
30850
30989
|
}
|
|
30851
|
-
|
|
30852
|
-
|
|
30853
|
-
|
|
30854
|
-
|
|
30855
|
-
|
|
30856
|
-
|
|
30857
|
-
|
|
30858
|
-
|
|
30990
|
+
|
|
30991
|
+
/**
|
|
30992
|
+
* Check if running on server
|
|
30993
|
+
* Uses a type-safe approach that works without DOM types
|
|
30994
|
+
*/
|
|
30995
|
+
export function isServer(): boolean {
|
|
30996
|
+
return !(typeof window !== 'undefined' && window.document);
|
|
30997
|
+
}
|
|
30998
|
+
|
|
30999
|
+
/**
|
|
31000
|
+
* Check if running on client
|
|
31001
|
+
* Uses a type-safe approach that works without DOM types
|
|
31002
|
+
*/
|
|
31003
|
+
export function isClient(): boolean {
|
|
31004
|
+
return typeof window !== 'undefined' && !!window.document;
|
|
31005
|
+
}
|
|
31006
|
+
|
|
31007
|
+
// Declare window for environments without DOM types
|
|
31008
|
+
declare const window: { document?: unknown } | undefined;
|
|
31009
|
+
`);
|
|
31010
|
+
}
|
|
31011
|
+
|
|
31012
|
+
// ../templates/src/builders/full-v2.ts
|
|
31013
|
+
async function buildFullPresetV2(projectPath, context) {
|
|
31014
|
+
const preset = PresetRegistry.get("nextjs-monorepo");
|
|
31015
|
+
if (!preset) {
|
|
31016
|
+
throw new Error("nextjs-monorepo preset not found in registry");
|
|
31017
|
+
}
|
|
31018
|
+
const scopeName = context.packageName;
|
|
31019
|
+
const directories = [
|
|
31020
|
+
"apps/web/src/app",
|
|
31021
|
+
"apps/web/src/components",
|
|
31022
|
+
"apps/web/src/lib",
|
|
31023
|
+
"apps/platform/src/app",
|
|
31024
|
+
"apps/platform/src/components",
|
|
31025
|
+
"apps/platform/src/lib",
|
|
31026
|
+
"apps/api/src/routes",
|
|
31027
|
+
"apps/api/src/middleware",
|
|
31028
|
+
"packages/ui",
|
|
31029
|
+
"packages/types",
|
|
31030
|
+
"packages/utils",
|
|
31031
|
+
"tooling/typescript"
|
|
31032
|
+
];
|
|
31033
|
+
for (const dir of directories) {
|
|
31034
|
+
await ensureDirectory(join(projectPath, dir));
|
|
31035
|
+
}
|
|
31036
|
+
const catalog = preset.catalogEntries || {};
|
|
31037
|
+
await writeMonorepoRootPackageJson(projectPath, scopeName, catalog, {
|
|
31038
|
+
hasWeb: true,
|
|
31039
|
+
hasPlatform: true,
|
|
31040
|
+
hasApi: true,
|
|
31041
|
+
codeQuality: context.codeQuality === "biome" ? "biome" : "ultracite"
|
|
31042
|
+
});
|
|
31043
|
+
await writeFile(join(projectPath, "bunfig.toml"), generateBunfigContent(context));
|
|
31044
|
+
await writeNextjsAppPackageJson(join(projectPath, "apps/web"), scopeName, "web", {
|
|
31045
|
+
usesUi: true,
|
|
31046
|
+
usesTypes: true,
|
|
31047
|
+
shadcnIconLibrary: context.shadcnIconLibrary || "iconoir"
|
|
31048
|
+
});
|
|
31049
|
+
await writeFile(join(projectPath, "apps/web/src/app/layout.tsx"), `import type { Metadata } from 'next';
|
|
31050
|
+
import '@${scopeName}/ui/globals.css';
|
|
30859
31051
|
|
|
30860
31052
|
export const metadata: Metadata = {
|
|
30861
31053
|
title: '${context.projectName}',
|
|
30862
|
-
description: '
|
|
30863
|
-
}
|
|
31054
|
+
description: 'Enterprise SaaS built with bunkit',
|
|
31055
|
+
};
|
|
30864
31056
|
|
|
30865
31057
|
export default function RootLayout({
|
|
30866
31058
|
children,
|
|
30867
31059
|
}: {
|
|
30868
|
-
children: React.ReactNode
|
|
31060
|
+
children: React.ReactNode;
|
|
30869
31061
|
}) {
|
|
30870
31062
|
return (
|
|
30871
31063
|
<html lang="en">
|
|
30872
|
-
<body>{children}</body>
|
|
31064
|
+
<body className="antialiased">{children}</body>
|
|
30873
31065
|
</html>
|
|
30874
|
-
)
|
|
31066
|
+
);
|
|
30875
31067
|
}
|
|
30876
|
-
|
|
30877
|
-
await writeFile(join(projectPath, "src/app/
|
|
30878
|
-
|
|
31068
|
+
`);
|
|
31069
|
+
await writeFile(join(projectPath, "apps/web/src/app/page.tsx"), `import { Home, Rocket, Book } from 'iconoir-react';
|
|
31070
|
+
|
|
31071
|
+
export default function HomePage() {
|
|
30879
31072
|
return (
|
|
30880
|
-
<main className="min-h-screen flex items-center justify-center">
|
|
30881
|
-
<div className="text-center">
|
|
30882
|
-
<
|
|
30883
|
-
|
|
31073
|
+
<main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-background to-muted">
|
|
31074
|
+
<div className="text-center space-y-8 p-8 max-w-3xl">
|
|
31075
|
+
<div className="flex justify-center">
|
|
31076
|
+
<Home className="w-16 h-16 text-primary" />
|
|
31077
|
+
</div>
|
|
31078
|
+
<h1 className="text-5xl font-bold tracking-tight">
|
|
31079
|
+
Welcome to ${context.projectName}
|
|
30884
31080
|
</h1>
|
|
30885
|
-
<p className="text-
|
|
30886
|
-
|
|
31081
|
+
<p className="text-xl text-muted-foreground">
|
|
31082
|
+
Enterprise-grade SaaS built with Next.js 16, React 19, Hono, and Bun
|
|
30887
31083
|
</p>
|
|
31084
|
+
<div className="flex gap-4 justify-center pt-4">
|
|
31085
|
+
<a
|
|
31086
|
+
href="/dashboard"
|
|
31087
|
+
className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition font-medium"
|
|
31088
|
+
>
|
|
31089
|
+
<Rocket className="w-5 h-5" />
|
|
31090
|
+
Get Started
|
|
31091
|
+
</a>
|
|
31092
|
+
<a
|
|
31093
|
+
href="/docs"
|
|
31094
|
+
className="inline-flex items-center gap-2 px-6 py-3 bg-secondary text-secondary-foreground rounded-lg hover:bg-secondary/90 transition font-medium"
|
|
31095
|
+
>
|
|
31096
|
+
<Book className="w-5 h-5" />
|
|
31097
|
+
Documentation
|
|
31098
|
+
</a>
|
|
31099
|
+
</div>
|
|
30888
31100
|
</div>
|
|
30889
31101
|
</main>
|
|
30890
|
-
)
|
|
31102
|
+
);
|
|
30891
31103
|
}
|
|
30892
|
-
|
|
30893
|
-
|
|
30894
|
-
// When you add dynamic routes with params, make your component async and await params:
|
|
30895
|
-
// export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|
30896
|
-
// const { slug } = await params;
|
|
30897
|
-
// return <div>{slug}</div>;
|
|
30898
|
-
// }
|
|
30899
|
-
`;
|
|
30900
|
-
await writeFile(join(projectPath, "src/app/page.tsx"), pageContent);
|
|
30901
|
-
const globalsCssContent = `@import "tailwindcss";
|
|
30902
|
-
`;
|
|
30903
|
-
await writeFile(join(projectPath, "src/app/globals.css"), globalsCssContent);
|
|
30904
|
-
const nextConfigContent = `import type { NextConfig } from 'next';
|
|
31104
|
+
`);
|
|
31105
|
+
await writeFile(join(projectPath, "apps/web/next.config.ts"), `import type { NextConfig } from 'next';
|
|
30905
31106
|
|
|
30906
31107
|
const nextConfig: NextConfig = {
|
|
30907
|
-
|
|
31108
|
+
transpilePackages: ['@${scopeName}/ui'],
|
|
30908
31109
|
};
|
|
30909
31110
|
|
|
30910
31111
|
export default nextConfig;
|
|
30911
|
-
|
|
30912
|
-
await writeFile(join(projectPath, "
|
|
30913
|
-
|
|
30914
|
-
|
|
30915
|
-
const config: Config = {
|
|
30916
|
-
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
|
30917
|
-
theme: {
|
|
30918
|
-
extend: {},
|
|
30919
|
-
},
|
|
30920
|
-
plugins: [],
|
|
30921
|
-
};
|
|
30922
|
-
|
|
30923
|
-
export default config;
|
|
30924
|
-
`;
|
|
30925
|
-
await writeFile(join(projectPath, "tailwind.config.ts"), tailwindConfigContent);
|
|
30926
|
-
const getTsCompilerOptions = () => {
|
|
30927
|
-
const baseOptions = {
|
|
30928
|
-
target: "ES2017",
|
|
30929
|
-
lib: ["dom", "dom.iterable", "esnext"],
|
|
30930
|
-
allowJs: true,
|
|
30931
|
-
skipLibCheck: true,
|
|
30932
|
-
noEmit: true,
|
|
30933
|
-
esModuleInterop: true,
|
|
30934
|
-
module: "esnext",
|
|
30935
|
-
moduleResolution: "bundler",
|
|
30936
|
-
resolveJsonModule: true,
|
|
30937
|
-
isolatedModules: true,
|
|
30938
|
-
jsx: "react-jsx",
|
|
30939
|
-
incremental: true,
|
|
30940
|
-
plugins: [{ name: "next" }],
|
|
30941
|
-
paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
|
|
30942
|
-
};
|
|
30943
|
-
if (context.tsStrictness === "strict") {
|
|
30944
|
-
return {
|
|
30945
|
-
...baseOptions,
|
|
30946
|
-
strict: true,
|
|
30947
|
-
noUnusedLocals: true,
|
|
30948
|
-
noUnusedParameters: true,
|
|
30949
|
-
noFallthroughCasesInSwitch: true,
|
|
30950
|
-
noImplicitReturns: true
|
|
30951
|
-
};
|
|
30952
|
-
}
|
|
30953
|
-
if (context.tsStrictness === "moderate") {
|
|
30954
|
-
return {
|
|
30955
|
-
...baseOptions,
|
|
30956
|
-
strict: true,
|
|
30957
|
-
noUnusedLocals: false,
|
|
30958
|
-
noUnusedParameters: false
|
|
30959
|
-
};
|
|
30960
|
-
}
|
|
30961
|
-
return {
|
|
30962
|
-
...baseOptions,
|
|
30963
|
-
strict: false,
|
|
30964
|
-
noImplicitAny: false
|
|
30965
|
-
};
|
|
30966
|
-
};
|
|
30967
|
-
const tsconfigContent = {
|
|
30968
|
-
compilerOptions: getTsCompilerOptions(),
|
|
30969
|
-
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
|
|
30970
|
-
exclude: ["node_modules"]
|
|
30971
|
-
};
|
|
30972
|
-
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
|
|
30973
|
-
const bunfigContent = generateBunfigContent(context);
|
|
30974
|
-
await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
|
|
30975
|
-
if (context.codeQuality === "ultracite") {
|
|
30976
|
-
await setupUltracite(projectPath, context);
|
|
30977
|
-
} else {
|
|
30978
|
-
await setupBiome(projectPath, context);
|
|
30979
|
-
}
|
|
30980
|
-
if (context.docker) {
|
|
30981
|
-
await setupDocker(projectPath, context);
|
|
30982
|
-
}
|
|
30983
|
-
if (context.cicd) {
|
|
30984
|
-
await setupGitHubActions(projectPath, context);
|
|
30985
|
-
}
|
|
30986
|
-
if (context.uiLibrary === "shadcn" && context.cssFramework === "tailwind") {
|
|
30987
|
-
await setupShadcnWeb(projectPath, context);
|
|
30988
|
-
}
|
|
30989
|
-
await setupVSCodeDebug(projectPath, context, "web");
|
|
30990
|
-
const packageJsonPath = join(projectPath, "package.json");
|
|
30991
|
-
const existingPackageJson = JSON.parse(await Bun.file(packageJsonPath).text());
|
|
30992
|
-
existingPackageJson.dependencies = {
|
|
30993
|
-
react: "^19.2.3",
|
|
30994
|
-
"react-dom": "^19.2.3",
|
|
30995
|
-
next: "^16.0.10",
|
|
30996
|
-
...existingPackageJson.dependencies
|
|
30997
|
-
};
|
|
30998
|
-
existingPackageJson.devDependencies = {
|
|
30999
|
-
...existingPackageJson.devDependencies,
|
|
31000
|
-
"@types/react": "^19.2.7",
|
|
31001
|
-
"@types/react-dom": "^19.2.3",
|
|
31002
|
-
"@types/node": "^25.0.3"
|
|
31003
|
-
};
|
|
31004
|
-
if (!existingPackageJson.scripts) {
|
|
31005
|
-
existingPackageJson.scripts = {};
|
|
31006
|
-
}
|
|
31007
|
-
if (!existingPackageJson.scripts.dev) {
|
|
31008
|
-
existingPackageJson.scripts.dev = "bun run --bun next dev --turbopack";
|
|
31009
|
-
}
|
|
31010
|
-
if (!existingPackageJson.scripts.build) {
|
|
31011
|
-
existingPackageJson.scripts.build = "bun run --bun next build";
|
|
31012
|
-
}
|
|
31013
|
-
if (!existingPackageJson.scripts.start) {
|
|
31014
|
-
existingPackageJson.scripts.start = "bun run --bun next start";
|
|
31015
|
-
}
|
|
31016
|
-
if (!existingPackageJson.scripts.debug) {
|
|
31017
|
-
existingPackageJson.scripts.debug = "bun --inspect node_modules/.bin/next dev --turbopack";
|
|
31018
|
-
}
|
|
31019
|
-
if (!existingPackageJson.scripts["debug:brk"]) {
|
|
31020
|
-
existingPackageJson.scripts["debug:brk"] = "bun --inspect-brk node_modules/.bin/next dev --turbopack";
|
|
31021
|
-
}
|
|
31022
|
-
if (!existingPackageJson.scripts["debug:wait"]) {
|
|
31023
|
-
existingPackageJson.scripts["debug:wait"] = "bun --inspect-wait node_modules/.bin/next dev --turbopack";
|
|
31024
|
-
}
|
|
31025
|
-
await writeFile(packageJsonPath, JSON.stringify(existingPackageJson, null, 2));
|
|
31026
|
-
await generateNextjsReadme(projectPath, context);
|
|
31027
|
-
}
|
|
31028
|
-
// ../templates/src/builders/full-v2.ts
|
|
31029
|
-
init_src();
|
|
31030
|
-
init_dist();
|
|
31031
|
-
init_package_json();
|
|
31032
|
-
|
|
31033
|
-
// ../templates/src/shared/ui-package.ts
|
|
31034
|
-
init_src();
|
|
31035
|
-
init_dist();
|
|
31036
|
-
init_package_json();
|
|
31037
|
-
function isModernStyle(style) {
|
|
31038
|
-
return style === "radix-maia" || style === "radix-vega" || style === "radix-nova" || style === "radix-lyra" || style === "radix-mira";
|
|
31039
|
-
}
|
|
31040
|
-
async function buildUiPackage(packagesPath, options) {
|
|
31041
|
-
const uiPath = join(packagesPath, "ui");
|
|
31042
|
-
await ensureDirectory(join(uiPath, "src/components"));
|
|
31043
|
-
await ensureDirectory(join(uiPath, "src/hooks"));
|
|
31044
|
-
await ensureDirectory(join(uiPath, "src/lib"));
|
|
31045
|
-
await ensureDirectory(join(uiPath, "src/styles"));
|
|
31046
|
-
await writeUiPackageJson(uiPath, options.scopeName);
|
|
31047
|
-
await writeFile(join(uiPath, "tsconfig.json"), JSON.stringify({
|
|
31048
|
-
extends: "../../tooling/typescript/library.json",
|
|
31112
|
+
`);
|
|
31113
|
+
await writeFile(join(projectPath, "apps/web/tsconfig.json"), JSON.stringify({
|
|
31114
|
+
extends: "../../tooling/typescript/nextjs.json",
|
|
31049
31115
|
compilerOptions: {
|
|
31050
|
-
jsx: "react-jsx",
|
|
31051
|
-
rootDir: "./src",
|
|
31052
|
-
outDir: "./dist",
|
|
31053
|
-
baseUrl: ".",
|
|
31054
31116
|
paths: {
|
|
31055
31117
|
"@/*": ["./src/*"]
|
|
31056
31118
|
}
|
|
31057
31119
|
},
|
|
31058
|
-
include: ["
|
|
31120
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
31059
31121
|
exclude: ["node_modules"]
|
|
31060
31122
|
}, null, 2));
|
|
31061
|
-
|
|
31062
|
-
const useModernStyle = isModernStyle(style);
|
|
31063
|
-
const iconLibrary = options.shadcnIconLibrary || (useModernStyle ? "phosphor" : "iconoir");
|
|
31064
|
-
const componentsJson = {
|
|
31065
|
-
$schema: "https://ui.shadcn.com/schema.json",
|
|
31066
|
-
style,
|
|
31067
|
-
rsc: true,
|
|
31068
|
-
tsx: true,
|
|
31069
|
-
tailwind: {
|
|
31070
|
-
config: "",
|
|
31071
|
-
css: "./src/styles/globals.css",
|
|
31072
|
-
baseColor: options.shadcnBaseColor || "zinc",
|
|
31073
|
-
cssVariables: true,
|
|
31074
|
-
prefix: ""
|
|
31075
|
-
},
|
|
31076
|
-
iconLibrary,
|
|
31077
|
-
aliases: {
|
|
31078
|
-
components: "@/components",
|
|
31079
|
-
utils: "@/lib/utils",
|
|
31080
|
-
ui: "@/components/ui",
|
|
31081
|
-
lib: "@/lib",
|
|
31082
|
-
hooks: "@/hooks"
|
|
31083
|
-
}
|
|
31084
|
-
};
|
|
31085
|
-
if (useModernStyle) {
|
|
31086
|
-
componentsJson.menuColor = options.shadcnMenuColor || "default";
|
|
31087
|
-
componentsJson.menuAccent = options.shadcnMenuAccent || "subtle";
|
|
31088
|
-
componentsJson.registries = {};
|
|
31089
|
-
}
|
|
31090
|
-
await writeFile(join(uiPath, "components.json"), JSON.stringify(componentsJson, null, 2));
|
|
31091
|
-
await writeFile(join(uiPath, "postcss.config.mjs"), `export default {
|
|
31123
|
+
await writeFile(join(projectPath, "apps/web/postcss.config.mjs"), `export default {
|
|
31092
31124
|
plugins: {
|
|
31093
31125
|
'@tailwindcss/postcss': {},
|
|
31094
31126
|
},
|
|
31095
31127
|
};
|
|
31096
31128
|
`);
|
|
31097
|
-
|
|
31098
|
-
|
|
31099
|
-
|
|
31100
|
-
|
|
31101
|
-
|
|
31102
|
-
|
|
31103
|
-
|
|
31104
|
-
globalsCss = `@import "tailwindcss";
|
|
31105
|
-
@import "tw-animate-css";
|
|
31106
|
-
@import "shadcn/tailwind.css";
|
|
31107
|
-
${appSourcePaths.join(`
|
|
31108
|
-
`)}
|
|
31109
|
-
@source "../**/*.{ts,tsx}";
|
|
31110
|
-
|
|
31111
|
-
@custom-variant dark (&:is(.dark *));
|
|
31112
|
-
|
|
31113
|
-
${modernThemeCSS}
|
|
31114
|
-
`;
|
|
31115
|
-
} else {
|
|
31116
|
-
const theme = themes[baseColor] || themes.zinc;
|
|
31117
|
-
const themeCSS = generateThemeCSS(theme, customRadius);
|
|
31118
|
-
globalsCss = `@import "tailwindcss";
|
|
31119
|
-
${appSourcePaths.join(`
|
|
31120
|
-
`)}
|
|
31121
|
-
@source "../**/*.{ts,tsx}";
|
|
31129
|
+
await writeNextjsAppPackageJson(join(projectPath, "apps/platform"), scopeName, "platform", {
|
|
31130
|
+
usesUi: true,
|
|
31131
|
+
usesTypes: true,
|
|
31132
|
+
shadcnIconLibrary: context.shadcnIconLibrary || "iconoir"
|
|
31133
|
+
});
|
|
31134
|
+
await writeFile(join(projectPath, "apps/platform/src/app/layout.tsx"), `import type { Metadata } from 'next';
|
|
31135
|
+
import '@${scopeName}/ui/globals.css';
|
|
31122
31136
|
|
|
31123
|
-
|
|
31124
|
-
|
|
31125
|
-
}
|
|
31126
|
-
|
|
31127
|
-
await writeFile(join(uiPath, "src/lib/utils.ts"), `import { clsx, type ClassValue } from 'clsx';
|
|
31128
|
-
import { twMerge } from 'tailwind-merge';
|
|
31137
|
+
export const metadata: Metadata = {
|
|
31138
|
+
title: '${context.projectName} - Admin',
|
|
31139
|
+
description: 'Admin dashboard for ${context.projectName}',
|
|
31140
|
+
};
|
|
31129
31141
|
|
|
31130
|
-
export function
|
|
31131
|
-
|
|
31142
|
+
export default function RootLayout({
|
|
31143
|
+
children,
|
|
31144
|
+
}: {
|
|
31145
|
+
children: React.ReactNode;
|
|
31146
|
+
}) {
|
|
31147
|
+
return (
|
|
31148
|
+
<html lang="en">
|
|
31149
|
+
<body className="antialiased">{children}</body>
|
|
31150
|
+
</html>
|
|
31151
|
+
);
|
|
31132
31152
|
}
|
|
31133
31153
|
`);
|
|
31134
|
-
await writeFile(join(
|
|
31135
|
-
* UI Components
|
|
31136
|
-
*
|
|
31137
|
-
* Add shadcn/ui components using:
|
|
31138
|
-
* bunx shadcn@latest add button card dialog ...
|
|
31139
|
-
*
|
|
31140
|
-
* Then export them here for use across apps.
|
|
31141
|
-
*/
|
|
31142
|
-
|
|
31143
|
-
// Export components as they are added
|
|
31144
|
-
// Example: export { Button } from './ui/button';
|
|
31145
|
-
|
|
31146
|
-
// Empty export to make this a valid ES module
|
|
31147
|
-
export {};
|
|
31148
|
-
`);
|
|
31149
|
-
await writeFile(join(uiPath, "src/hooks/index.ts"), `/**
|
|
31150
|
-
* Shared Hooks
|
|
31151
|
-
*
|
|
31152
|
-
* Custom React hooks shared across apps.
|
|
31153
|
-
*/
|
|
31154
|
+
await writeFile(join(projectPath, "apps/platform/src/app/page.tsx"), `import { ViewGrid, User, Dollar, GraphUp } from 'iconoir-react';
|
|
31154
31155
|
|
|
31155
|
-
|
|
31156
|
-
|
|
31156
|
+
export default function DashboardPage() {
|
|
31157
|
+
return (
|
|
31158
|
+
<main className="min-h-screen bg-background">
|
|
31159
|
+
<div className="max-w-7xl mx-auto py-12 px-4">
|
|
31160
|
+
<header className="mb-8 flex items-center gap-3">
|
|
31161
|
+
<ViewGrid className="w-8 h-8 text-primary" />
|
|
31162
|
+
<div>
|
|
31163
|
+
<h1 className="text-3xl font-bold">Admin Dashboard</h1>
|
|
31164
|
+
<p className="text-muted-foreground">
|
|
31165
|
+
Manage your ${context.projectName} platform
|
|
31166
|
+
</p>
|
|
31167
|
+
</div>
|
|
31168
|
+
</header>
|
|
31157
31169
|
|
|
31158
|
-
|
|
31159
|
-
|
|
31160
|
-
|
|
31161
|
-
|
|
31162
|
-
|
|
31163
|
-
|
|
31164
|
-
|
|
31165
|
-
|
|
31166
|
-
|
|
31167
|
-
|
|
31168
|
-
|
|
31169
|
-
|
|
31170
|
-
|
|
31171
|
-
|
|
31172
|
-
|
|
31173
|
-
|
|
31170
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
31171
|
+
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31172
|
+
<div className="flex items-center gap-3 mb-2">
|
|
31173
|
+
<User className="w-5 h-5 text-blue-500" />
|
|
31174
|
+
<h2 className="text-lg font-semibold">Users</h2>
|
|
31175
|
+
</div>
|
|
31176
|
+
<p className="text-3xl font-bold">1,234</p>
|
|
31177
|
+
</div>
|
|
31178
|
+
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31179
|
+
<div className="flex items-center gap-3 mb-2">
|
|
31180
|
+
<Dollar className="w-5 h-5 text-green-500" />
|
|
31181
|
+
<h2 className="text-lg font-semibold">Revenue</h2>
|
|
31182
|
+
</div>
|
|
31183
|
+
<p className="text-3xl font-bold">$12,345</p>
|
|
31184
|
+
</div>
|
|
31185
|
+
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31186
|
+
<div className="flex items-center gap-3 mb-2">
|
|
31187
|
+
<GraphUp className="w-5 h-5 text-purple-500" />
|
|
31188
|
+
<h2 className="text-lg font-semibold">Active</h2>
|
|
31189
|
+
</div>
|
|
31190
|
+
<p className="text-3xl font-bold">567</p>
|
|
31191
|
+
</div>
|
|
31192
|
+
</div>
|
|
31193
|
+
</div>
|
|
31194
|
+
</main>
|
|
31195
|
+
);
|
|
31196
|
+
}
|
|
31197
|
+
`);
|
|
31198
|
+
await writeFile(join(projectPath, "apps/platform/next.config.ts"), `import type { NextConfig } from 'next';
|
|
31174
31199
|
|
|
31175
|
-
|
|
31176
|
-
|
|
31177
|
-
|
|
31200
|
+
const nextConfig: NextConfig = {
|
|
31201
|
+
transpilePackages: ['@${scopeName}/ui'],
|
|
31202
|
+
};
|
|
31203
|
+
|
|
31204
|
+
export default nextConfig;
|
|
31178
31205
|
`);
|
|
31179
|
-
|
|
31180
|
-
|
|
31181
|
-
const typesPath = join(packagesPath, "types");
|
|
31182
|
-
await ensureDirectory(join(typesPath, "src"));
|
|
31183
|
-
const { writeTypesPackageJson: writeTypesPackageJson2 } = await Promise.resolve().then(() => (init_package_json(), exports_package_json));
|
|
31184
|
-
await writeTypesPackageJson2(typesPath, scopeName);
|
|
31185
|
-
await writeFile(join(typesPath, "tsconfig.json"), JSON.stringify({
|
|
31186
|
-
extends: "../../tooling/typescript/library.json",
|
|
31206
|
+
await writeFile(join(projectPath, "apps/platform/tsconfig.json"), JSON.stringify({
|
|
31207
|
+
extends: "../../tooling/typescript/nextjs.json",
|
|
31187
31208
|
compilerOptions: {
|
|
31188
|
-
|
|
31189
|
-
|
|
31209
|
+
paths: {
|
|
31210
|
+
"@/*": ["./src/*"]
|
|
31211
|
+
}
|
|
31190
31212
|
},
|
|
31191
|
-
include: ["
|
|
31213
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
31192
31214
|
exclude: ["node_modules"]
|
|
31193
31215
|
}, null, 2));
|
|
31194
|
-
await writeFile(join(
|
|
31195
|
-
|
|
31196
|
-
|
|
31197
|
-
|
|
31198
|
-
|
|
31216
|
+
await writeFile(join(projectPath, "apps/platform/postcss.config.mjs"), `export default {
|
|
31217
|
+
plugins: {
|
|
31218
|
+
'@tailwindcss/postcss': {},
|
|
31219
|
+
},
|
|
31220
|
+
};
|
|
31221
|
+
`);
|
|
31222
|
+
await writeHonoApiPackageJson(join(projectPath, "apps/api"), scopeName, {
|
|
31223
|
+
usesTypes: true
|
|
31224
|
+
});
|
|
31225
|
+
await writeFile(join(projectPath, "apps/api/src/index.ts"), `import { Hono } from 'hono';
|
|
31226
|
+
import { logger } from 'hono/logger';
|
|
31227
|
+
import { cors } from 'hono/cors';
|
|
31199
31228
|
|
|
31200
|
-
|
|
31229
|
+
const app = new Hono();
|
|
31201
31230
|
|
|
31202
|
-
|
|
31203
|
-
|
|
31204
|
-
|
|
31205
|
-
name?: string;
|
|
31206
|
-
createdAt: Date;
|
|
31207
|
-
updatedAt: Date;
|
|
31208
|
-
}
|
|
31231
|
+
// Middleware
|
|
31232
|
+
app.use('*', logger());
|
|
31233
|
+
app.use('*', cors());
|
|
31209
31234
|
|
|
31210
|
-
|
|
31211
|
-
|
|
31212
|
-
|
|
31213
|
-
|
|
31214
|
-
|
|
31215
|
-
|
|
31216
|
-
};
|
|
31217
|
-
}
|
|
31235
|
+
// Routes
|
|
31236
|
+
app.get('/', (context) => {
|
|
31237
|
+
return context.json({
|
|
31238
|
+
name: '${context.projectName} API',
|
|
31239
|
+
version: '1.0.0',
|
|
31240
|
+
timestamp: new Date().toISOString(),
|
|
31241
|
+
});
|
|
31242
|
+
});
|
|
31218
31243
|
|
|
31219
|
-
|
|
31220
|
-
|
|
31221
|
-
|
|
31222
|
-
|
|
31223
|
-
|
|
31224
|
-
|
|
31225
|
-
|
|
31226
|
-
}
|
|
31244
|
+
app.get('/health', (context) => {
|
|
31245
|
+
return context.json({ status: 'ok' });
|
|
31246
|
+
});
|
|
31247
|
+
|
|
31248
|
+
app.get('/api/users', (context) => {
|
|
31249
|
+
return context.json({
|
|
31250
|
+
users: [
|
|
31251
|
+
{ id: '1', email: 'john@example.com', name: 'John Doe' },
|
|
31252
|
+
{ id: '2', email: 'jane@example.com', name: 'Jane Smith' },
|
|
31253
|
+
],
|
|
31254
|
+
});
|
|
31255
|
+
});
|
|
31256
|
+
|
|
31257
|
+
// 404 handler
|
|
31258
|
+
app.notFound((context) => {
|
|
31259
|
+
return context.json({ error: 'Not found' }, 404);
|
|
31260
|
+
});
|
|
31261
|
+
|
|
31262
|
+
// Error handler
|
|
31263
|
+
app.onError((error, context) => {
|
|
31264
|
+
console.error('Error:', error);
|
|
31265
|
+
return context.json({ error: 'Internal server error' }, 500);
|
|
31266
|
+
});
|
|
31267
|
+
|
|
31268
|
+
// Start server with HMR
|
|
31269
|
+
const server = Bun.serve({
|
|
31270
|
+
fetch: app.fetch,
|
|
31271
|
+
port: 3002,
|
|
31272
|
+
development: {
|
|
31273
|
+
hmr: true,
|
|
31274
|
+
},
|
|
31275
|
+
});
|
|
31276
|
+
|
|
31277
|
+
console.log(\`\uD83D\uDE80 ${context.projectName} API running on \${server.url}\`);
|
|
31278
|
+
|
|
31279
|
+
export default app;
|
|
31227
31280
|
`);
|
|
31228
|
-
|
|
31229
|
-
|
|
31230
|
-
const utilsPath = join(packagesPath, "utils");
|
|
31231
|
-
await ensureDirectory(join(utilsPath, "src"));
|
|
31232
|
-
const { writeUtilsPackageJson: writeUtilsPackageJson2 } = await Promise.resolve().then(() => (init_package_json(), exports_package_json));
|
|
31233
|
-
await writeUtilsPackageJson2(utilsPath, scopeName);
|
|
31234
|
-
await writeFile(join(utilsPath, "tsconfig.json"), JSON.stringify({
|
|
31235
|
-
extends: "../../tooling/typescript/library.json",
|
|
31281
|
+
await writeFile(join(projectPath, "apps/api/tsconfig.json"), JSON.stringify({
|
|
31282
|
+
extends: "../../tooling/typescript/api.json",
|
|
31236
31283
|
compilerOptions: {
|
|
31237
|
-
|
|
31238
|
-
outDir: "./dist"
|
|
31284
|
+
types: ["bun-types"]
|
|
31239
31285
|
},
|
|
31240
31286
|
include: ["src/**/*"],
|
|
31241
31287
|
exclude: ["node_modules"]
|
|
31242
31288
|
}, null, 2));
|
|
31243
|
-
await
|
|
31244
|
-
|
|
31245
|
-
|
|
31246
|
-
|
|
31247
|
-
|
|
31248
|
-
|
|
31249
|
-
|
|
31250
|
-
|
|
31251
|
-
|
|
31252
|
-
|
|
31253
|
-
|
|
31254
|
-
return d.toLocaleDateString(locale, {
|
|
31255
|
-
year: 'numeric',
|
|
31256
|
-
month: 'long',
|
|
31257
|
-
day: 'numeric',
|
|
31289
|
+
await buildUiPackage(join(projectPath, "packages"), {
|
|
31290
|
+
scopeName,
|
|
31291
|
+
shadcnStyle: context.shadcnStyle || "radix-maia",
|
|
31292
|
+
shadcnBase: context.shadcnBase || "radix",
|
|
31293
|
+
shadcnBaseColor: context.shadcnBaseColor || "zinc",
|
|
31294
|
+
shadcnIconLibrary: context.shadcnIconLibrary || "iconoir",
|
|
31295
|
+
shadcnMenuAccent: context.shadcnMenuAccent || "subtle",
|
|
31296
|
+
shadcnMenuColor: context.shadcnMenuColor || "default",
|
|
31297
|
+
shadcnRadius: context.shadcnRadius || "0.625rem",
|
|
31298
|
+
shadcnRtl: context.shadcnRtl,
|
|
31299
|
+
appsToScan: ["web", "platform"]
|
|
31258
31300
|
});
|
|
31301
|
+
await buildTypesPackage(join(projectPath, "packages"), scopeName);
|
|
31302
|
+
await buildUtilsPackage(join(projectPath, "packages"), scopeName);
|
|
31303
|
+
await setupTooling(projectPath, context);
|
|
31304
|
+
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify({
|
|
31305
|
+
extends: "./tooling/typescript/base.json",
|
|
31306
|
+
include: ["apps/*/src/**/*", "packages/*/src/**/*"],
|
|
31307
|
+
exclude: ["node_modules", "**/node_modules", "**/.next"]
|
|
31308
|
+
}, null, 2));
|
|
31309
|
+
if (context.codeQuality === "ultracite") {
|
|
31310
|
+
await setupUltracite(projectPath, context);
|
|
31311
|
+
} else {
|
|
31312
|
+
await setupBiome(projectPath, context);
|
|
31313
|
+
}
|
|
31314
|
+
if (context.docker) {
|
|
31315
|
+
await setupDocker(projectPath, context);
|
|
31316
|
+
}
|
|
31317
|
+
if (context.cicd) {
|
|
31318
|
+
await setupGitHubActions(projectPath, context);
|
|
31319
|
+
}
|
|
31320
|
+
await setupVSCodeDebug(projectPath, context, "full");
|
|
31321
|
+
await generateMonorepoReadme(projectPath, context, "nextjs");
|
|
31259
31322
|
}
|
|
31323
|
+
// ../templates/src/builders/minimal.ts
|
|
31324
|
+
init_src();
|
|
31325
|
+
init_dist();
|
|
31326
|
+
async function buildMinimalPreset(projectPath, context) {
|
|
31327
|
+
const indexContent = `console.log('Hello from ${context.projectName}! \uD83C\uDF5E');
|
|
31260
31328
|
|
|
31261
|
-
|
|
31262
|
-
|
|
31263
|
-
|
|
31264
|
-
|
|
31265
|
-
amount: number,
|
|
31266
|
-
currency = 'USD',
|
|
31267
|
-
locale = 'en-US'
|
|
31268
|
-
): string {
|
|
31269
|
-
return new Intl.NumberFormat(locale, {
|
|
31270
|
-
style: 'currency',
|
|
31271
|
-
currency,
|
|
31272
|
-
}).format(amount);
|
|
31273
|
-
}
|
|
31274
|
-
|
|
31275
|
-
/**
|
|
31276
|
-
* Sleep for a specified number of milliseconds
|
|
31277
|
-
*/
|
|
31278
|
-
export function sleep(ms: number): Promise<void> {
|
|
31279
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31280
|
-
}
|
|
31329
|
+
// Your code here
|
|
31330
|
+
const greet = (name: string): void => {
|
|
31331
|
+
console.log(\`Welcome, \${name}!\`);
|
|
31332
|
+
};
|
|
31281
31333
|
|
|
31282
|
-
|
|
31283
|
-
|
|
31284
|
-
|
|
31285
|
-
|
|
31286
|
-
|
|
31287
|
-
|
|
31288
|
-
|
|
31289
|
-
|
|
31334
|
+
greet('Bun');
|
|
31335
|
+
`;
|
|
31336
|
+
await writeFile(join(projectPath, "src/index.ts"), indexContent);
|
|
31337
|
+
const tsconfigContent = {
|
|
31338
|
+
compilerOptions: {
|
|
31339
|
+
lib: ["ESNext"],
|
|
31340
|
+
target: "ESNext",
|
|
31341
|
+
module: "ESNext",
|
|
31342
|
+
moduleDetection: "force",
|
|
31343
|
+
jsx: "react-jsx",
|
|
31344
|
+
allowJs: true,
|
|
31345
|
+
moduleResolution: "bundler",
|
|
31346
|
+
allowImportingTsExtensions: true,
|
|
31347
|
+
verbatimModuleSyntax: true,
|
|
31348
|
+
noEmit: true,
|
|
31349
|
+
strict: true,
|
|
31350
|
+
skipLibCheck: true,
|
|
31351
|
+
noFallthroughCasesInSwitch: true,
|
|
31352
|
+
noUnusedLocals: false,
|
|
31353
|
+
noUnusedParameters: false,
|
|
31354
|
+
noPropertyAccessFromIndexSignature: false,
|
|
31355
|
+
types: ["bun-types"]
|
|
31356
|
+
}
|
|
31357
|
+
};
|
|
31358
|
+
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
|
|
31359
|
+
const bunfigContent = generateBunfigContent(context);
|
|
31360
|
+
await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
|
|
31361
|
+
if (context.codeQuality === "ultracite") {
|
|
31362
|
+
await setupUltracite(projectPath, context);
|
|
31363
|
+
} else {
|
|
31364
|
+
await setupBiome(projectPath, context);
|
|
31290
31365
|
}
|
|
31366
|
+
await setupVSCodeDebug(projectPath, context, "minimal");
|
|
31367
|
+
await generateMinimalReadme(projectPath, context);
|
|
31291
31368
|
}
|
|
31292
|
-
|
|
31293
|
-
|
|
31294
|
-
|
|
31295
|
-
|
|
31296
|
-
|
|
31297
|
-
|
|
31298
|
-
|
|
31299
|
-
|
|
31300
|
-
|
|
31369
|
+
// ../templates/src/builders/monorepo-bun.ts
|
|
31370
|
+
init_src();
|
|
31371
|
+
init_dist();
|
|
31372
|
+
var databaseSetupMap6 = {
|
|
31373
|
+
"postgres-drizzle": setupPostgresDrizzle,
|
|
31374
|
+
"postgres-prisma": setupPostgresPrisma,
|
|
31375
|
+
"mysql-drizzle": setupMySQLDrizzle,
|
|
31376
|
+
"mysql-prisma": setupMySQLPrisma,
|
|
31377
|
+
supabase: setupSupabaseOnly,
|
|
31378
|
+
"supabase-drizzle": setupSupabaseDrizzle,
|
|
31379
|
+
"supabase-prisma": setupSupabasePrisma,
|
|
31380
|
+
"sqlite-drizzle": setupSQLiteDrizzle,
|
|
31381
|
+
"sqlite-prisma": setupSQLitePrisma,
|
|
31382
|
+
none: async () => {}
|
|
31383
|
+
};
|
|
31384
|
+
async function buildMonorepoBunPreset(projectPath, context) {
|
|
31385
|
+
await ensureDirectory(join(projectPath, "apps/web"));
|
|
31386
|
+
await ensureDirectory(join(projectPath, "apps/api"));
|
|
31387
|
+
await ensureDirectory(join(projectPath, "packages/types"));
|
|
31388
|
+
await ensureDirectory(join(projectPath, "packages/utils"));
|
|
31389
|
+
if (context.database && context.database !== "none") {
|
|
31390
|
+
await ensureDirectory(join(projectPath, "packages/db"));
|
|
31301
31391
|
}
|
|
31302
|
-
|
|
31303
|
-
}
|
|
31304
|
-
|
|
31305
|
-
|
|
31306
|
-
|
|
31307
|
-
|
|
31308
|
-
|
|
31309
|
-
|
|
31310
|
-
|
|
31311
|
-
|
|
31312
|
-
|
|
31313
|
-
|
|
31314
|
-
|
|
31315
|
-
|
|
31392
|
+
const rootPackageJson = {
|
|
31393
|
+
name: `${context.packageName}-monorepo`,
|
|
31394
|
+
version: "0.0.0",
|
|
31395
|
+
private: true,
|
|
31396
|
+
workspaces: ["apps/*", "packages/*"],
|
|
31397
|
+
scripts: {
|
|
31398
|
+
dev: 'bun run --filter "*" dev',
|
|
31399
|
+
build: 'bun run --filter "*" build',
|
|
31400
|
+
lint: context.codeQuality === "biome" ? "biome check ." : "ultracite check .",
|
|
31401
|
+
format: context.codeQuality === "biome" ? "biome check --write ." : "ultracite fix .",
|
|
31402
|
+
test: "bun test",
|
|
31403
|
+
"dev:web": 'bun run --filter "@*/web" dev',
|
|
31404
|
+
"dev:api": 'bun run --filter "@*/api" dev',
|
|
31405
|
+
debug: "bun --inspect apps/api/src/index.ts",
|
|
31406
|
+
"debug:brk": "bun --inspect-brk apps/api/src/index.ts",
|
|
31407
|
+
"debug:wait": "bun --inspect-wait apps/api/src/index.ts"
|
|
31408
|
+
},
|
|
31409
|
+
devDependencies: {
|
|
31410
|
+
"@types/bun": "latest",
|
|
31411
|
+
typescript: "catalog:",
|
|
31412
|
+
...context.codeQuality === "biome" ? { "@biomejs/biome": "catalog:" } : {}
|
|
31413
|
+
},
|
|
31414
|
+
catalog: {
|
|
31415
|
+
react: "^19.2.3",
|
|
31416
|
+
"react-dom": "^19.2.3",
|
|
31417
|
+
"drizzle-orm": "^0.45.1",
|
|
31418
|
+
"drizzle-kit": "^0.31.8",
|
|
31419
|
+
postgres: "^3.4.7",
|
|
31420
|
+
"@supabase/supabase-js": "^2.88.0",
|
|
31421
|
+
"@prisma/client": "^7.2.0",
|
|
31422
|
+
prisma: "^7.2.0",
|
|
31423
|
+
mysql2: "^3.16.0",
|
|
31424
|
+
"better-auth": "^1.4.7",
|
|
31425
|
+
"next-auth": "^4.24.13",
|
|
31426
|
+
"@auth/drizzle-adapter": "^1.11.1",
|
|
31427
|
+
tailwindcss: "^4.1.18",
|
|
31428
|
+
autoprefixer: "^10.4.23",
|
|
31429
|
+
postcss: "^8.5.6",
|
|
31430
|
+
typescript: "^5.9.3",
|
|
31431
|
+
"@types/react": "^19.2.7",
|
|
31432
|
+
"@types/react-dom": "^19.2.3",
|
|
31433
|
+
"@types/node": "^25.0.3"
|
|
31434
|
+
}
|
|
31316
31435
|
};
|
|
31317
|
-
|
|
31318
|
-
|
|
31319
|
-
|
|
31320
|
-
|
|
31321
|
-
|
|
31322
|
-
|
|
31323
|
-
|
|
31324
|
-
|
|
31325
|
-
|
|
31326
|
-
|
|
31327
|
-
|
|
31328
|
-
|
|
31329
|
-
|
|
31330
|
-
|
|
31331
|
-
|
|
31332
|
-
|
|
31333
|
-
}
|
|
31334
|
-
|
|
31335
|
-
|
|
31336
|
-
|
|
31337
|
-
|
|
31338
|
-
}
|
|
31339
|
-
|
|
31340
|
-
|
|
31341
|
-
|
|
31342
|
-
const
|
|
31343
|
-
|
|
31344
|
-
|
|
31436
|
+
await writeFile(join(projectPath, "package.json"), JSON.stringify(rootPackageJson, null, 2));
|
|
31437
|
+
const apiPath = join(projectPath, "apps/api");
|
|
31438
|
+
await setupBunServeNative(apiPath, context, true);
|
|
31439
|
+
const apiPackageJson = {
|
|
31440
|
+
name: `@${context.packageName}/api`,
|
|
31441
|
+
version: "0.0.0",
|
|
31442
|
+
private: true,
|
|
31443
|
+
scripts: {
|
|
31444
|
+
dev: "bun run --hot src/index.ts",
|
|
31445
|
+
start: "bun run src/index.ts",
|
|
31446
|
+
debug: "bun --inspect src/index.ts",
|
|
31447
|
+
"debug:brk": "bun --inspect-brk src/index.ts",
|
|
31448
|
+
"debug:wait": "bun --inspect-wait src/index.ts"
|
|
31449
|
+
},
|
|
31450
|
+
dependencies: {
|
|
31451
|
+
[`@${context.packageName}/types`]: "workspace:*"
|
|
31452
|
+
},
|
|
31453
|
+
devDependencies: {
|
|
31454
|
+
"@types/bun": "latest",
|
|
31455
|
+
typescript: "catalog:"
|
|
31456
|
+
}
|
|
31457
|
+
};
|
|
31458
|
+
await writeFile(join(apiPath, "package.json"), JSON.stringify(apiPackageJson, null, 2));
|
|
31459
|
+
const webPath = join(projectPath, "apps/web");
|
|
31460
|
+
await setupBunFullstack(webPath, context);
|
|
31461
|
+
const webPackageJson = {
|
|
31462
|
+
name: `@${context.packageName}/web`,
|
|
31463
|
+
version: "0.0.0",
|
|
31464
|
+
private: true,
|
|
31465
|
+
scripts: {
|
|
31466
|
+
dev: "bun run --hot src/index.ts",
|
|
31467
|
+
build: "bun build src/index.ts --outdir ./dist --target bun",
|
|
31468
|
+
start: "bun run dist/index.js",
|
|
31469
|
+
debug: "bun --inspect src/index.ts",
|
|
31470
|
+
"debug:brk": "bun --inspect-brk src/index.ts",
|
|
31471
|
+
"debug:wait": "bun --inspect-wait src/index.ts"
|
|
31472
|
+
},
|
|
31473
|
+
dependencies: {
|
|
31474
|
+
react: "catalog:",
|
|
31475
|
+
"react-dom": "catalog:",
|
|
31476
|
+
[`@${context.packageName}/types`]: "workspace:*"
|
|
31477
|
+
},
|
|
31478
|
+
devDependencies: {
|
|
31479
|
+
"@types/react": "catalog:",
|
|
31480
|
+
"@types/react-dom": "catalog:",
|
|
31481
|
+
"@types/bun": "latest",
|
|
31482
|
+
typescript: "catalog:"
|
|
31483
|
+
}
|
|
31484
|
+
};
|
|
31485
|
+
await writeFile(join(webPath, "package.json"), JSON.stringify(webPackageJson, null, 2));
|
|
31486
|
+
if (context.database && context.database !== "none") {
|
|
31487
|
+
await databaseSetupMap6[context.database](join(projectPath, "packages/db"), context, true);
|
|
31345
31488
|
}
|
|
31346
|
-
|
|
31347
|
-
|
|
31348
|
-
"apps/web/src/app",
|
|
31349
|
-
"apps/web/src/components",
|
|
31350
|
-
"apps/web/src/lib",
|
|
31351
|
-
"apps/platform/src/app",
|
|
31352
|
-
"apps/platform/src/components",
|
|
31353
|
-
"apps/platform/src/lib",
|
|
31354
|
-
"apps/api/src/routes",
|
|
31355
|
-
"apps/api/src/middleware",
|
|
31356
|
-
"packages/ui",
|
|
31357
|
-
"packages/types",
|
|
31358
|
-
"packages/utils",
|
|
31359
|
-
"tooling/typescript"
|
|
31360
|
-
];
|
|
31361
|
-
for (const dir of directories) {
|
|
31362
|
-
await ensureDirectory(join(projectPath, dir));
|
|
31489
|
+
if (context.redis) {
|
|
31490
|
+
await setupRedis(join(projectPath, "packages"), context, true);
|
|
31363
31491
|
}
|
|
31364
|
-
|
|
31365
|
-
|
|
31366
|
-
|
|
31367
|
-
|
|
31368
|
-
|
|
31369
|
-
|
|
31370
|
-
|
|
31371
|
-
|
|
31372
|
-
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
|
|
31376
|
-
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
}
|
|
31380
|
-
|
|
31381
|
-
|
|
31382
|
-
|
|
31383
|
-
|
|
31384
|
-
|
|
31385
|
-
|
|
31386
|
-
|
|
31387
|
-
|
|
31388
|
-
|
|
31389
|
-
|
|
31390
|
-
|
|
31492
|
+
if (context.auth === "better-auth") {
|
|
31493
|
+
await setupBetterAuth(join(projectPath, "packages"), context, true);
|
|
31494
|
+
} else if (context.auth === "nextauth") {
|
|
31495
|
+
await setupNextAuth(join(projectPath, "packages"), context, true);
|
|
31496
|
+
}
|
|
31497
|
+
if (context.useBunSecrets) {
|
|
31498
|
+
await setupBunSecrets(join(projectPath, "packages"), context, true);
|
|
31499
|
+
}
|
|
31500
|
+
if (context.codeQuality === "biome") {
|
|
31501
|
+
await setupBiome(projectPath, context);
|
|
31502
|
+
} else {
|
|
31503
|
+
await setupUltracite(projectPath, context);
|
|
31504
|
+
}
|
|
31505
|
+
if (context.docker) {
|
|
31506
|
+
await setupDocker(projectPath, context);
|
|
31507
|
+
}
|
|
31508
|
+
if (context.cicd) {
|
|
31509
|
+
await setupGitHubActions(projectPath, context);
|
|
31510
|
+
}
|
|
31511
|
+
await setupTooling(projectPath, context);
|
|
31512
|
+
await setupVSCodeDebug(projectPath, context, "full");
|
|
31513
|
+
const bunfigContent = generateBunfigContent(context);
|
|
31514
|
+
await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
|
|
31515
|
+
const typesPackageJson = {
|
|
31516
|
+
name: `@${context.packageName}/types`,
|
|
31517
|
+
version: "0.0.0",
|
|
31518
|
+
private: true,
|
|
31519
|
+
main: "./index.ts",
|
|
31520
|
+
types: "./index.ts",
|
|
31521
|
+
scripts: {},
|
|
31522
|
+
devDependencies: {
|
|
31523
|
+
typescript: "catalog:"
|
|
31524
|
+
}
|
|
31525
|
+
};
|
|
31526
|
+
await ensureDirectory(join(projectPath, "packages/types"));
|
|
31527
|
+
await writeFile(join(projectPath, "packages/types/package.json"), JSON.stringify(typesPackageJson, null, 2));
|
|
31528
|
+
const typesIndex = `// Shared types across the monorepo
|
|
31529
|
+
export interface User {
|
|
31530
|
+
id: number;
|
|
31531
|
+
name: string;
|
|
31532
|
+
email: string;
|
|
31533
|
+
createdAt: Date;
|
|
31391
31534
|
}
|
|
31392
|
-
`);
|
|
31393
|
-
await writeFile(join(projectPath, "apps/web/src/app/page.tsx"), `import { Home, Rocket, Book } from 'iconoir-react';
|
|
31394
31535
|
|
|
31395
|
-
export
|
|
31396
|
-
|
|
31397
|
-
|
|
31398
|
-
|
|
31399
|
-
|
|
31400
|
-
|
|
31401
|
-
|
|
31402
|
-
|
|
31403
|
-
|
|
31404
|
-
|
|
31405
|
-
|
|
31406
|
-
|
|
31407
|
-
|
|
31408
|
-
|
|
31409
|
-
|
|
31410
|
-
|
|
31411
|
-
|
|
31412
|
-
|
|
31413
|
-
|
|
31414
|
-
|
|
31415
|
-
|
|
31416
|
-
|
|
31417
|
-
|
|
31418
|
-
|
|
31419
|
-
|
|
31420
|
-
<Book className="w-5 h-5" />
|
|
31421
|
-
Documentation
|
|
31422
|
-
</a>
|
|
31423
|
-
</div>
|
|
31424
|
-
</div>
|
|
31425
|
-
</main>
|
|
31426
|
-
);
|
|
31536
|
+
export interface ApiResponse<T = unknown> {
|
|
31537
|
+
success: boolean;
|
|
31538
|
+
data?: T;
|
|
31539
|
+
error?: string;
|
|
31540
|
+
timestamp: string;
|
|
31541
|
+
}
|
|
31542
|
+
`;
|
|
31543
|
+
await writeFile(join(projectPath, "packages/types/index.ts"), typesIndex);
|
|
31544
|
+
const utilsPackageJson = {
|
|
31545
|
+
name: `@${context.packageName}/utils`,
|
|
31546
|
+
version: "0.0.0",
|
|
31547
|
+
private: true,
|
|
31548
|
+
main: "./index.ts",
|
|
31549
|
+
types: "./index.ts",
|
|
31550
|
+
scripts: {},
|
|
31551
|
+
dependencies: {},
|
|
31552
|
+
devDependencies: {
|
|
31553
|
+
typescript: "catalog:"
|
|
31554
|
+
}
|
|
31555
|
+
};
|
|
31556
|
+
await ensureDirectory(join(projectPath, "packages/utils"));
|
|
31557
|
+
await writeFile(join(projectPath, "packages/utils/package.json"), JSON.stringify(utilsPackageJson, null, 2));
|
|
31558
|
+
const utilsIndex = `// Shared utilities across the monorepo
|
|
31559
|
+
export function formatDate(date: Date): string {
|
|
31560
|
+
return date.toISOString();
|
|
31427
31561
|
}
|
|
31428
|
-
`);
|
|
31429
|
-
await writeFile(join(projectPath, "apps/web/next.config.ts"), `import type { NextConfig } from 'next';
|
|
31430
|
-
|
|
31431
|
-
const nextConfig: NextConfig = {
|
|
31432
|
-
transpilePackages: ['@${scopeName}/ui'],
|
|
31433
|
-
};
|
|
31434
31562
|
|
|
31435
|
-
export
|
|
31436
|
-
|
|
31437
|
-
|
|
31438
|
-
|
|
31439
|
-
|
|
31440
|
-
|
|
31441
|
-
|
|
31442
|
-
|
|
31443
|
-
},
|
|
31444
|
-
include: ["
|
|
31563
|
+
export function capitalize(str: string): string {
|
|
31564
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
31565
|
+
}
|
|
31566
|
+
`;
|
|
31567
|
+
await writeFile(join(projectPath, "packages/utils/index.ts"), utilsIndex);
|
|
31568
|
+
await generateMonorepoReadme(projectPath, context, "bun");
|
|
31569
|
+
const tsconfigContent = {
|
|
31570
|
+
extends: "./tooling/typescript/base.json",
|
|
31571
|
+
compilerOptions: {},
|
|
31572
|
+
include: ["apps/*/src/**/*", "packages/*/**/*"],
|
|
31445
31573
|
exclude: ["node_modules"]
|
|
31446
|
-
}
|
|
31447
|
-
await writeFile(join(projectPath, "
|
|
31448
|
-
|
|
31449
|
-
|
|
31450
|
-
|
|
31451
|
-
|
|
31452
|
-
|
|
31453
|
-
await
|
|
31454
|
-
await
|
|
31455
|
-
import
|
|
31574
|
+
};
|
|
31575
|
+
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
|
|
31576
|
+
}
|
|
31577
|
+
// ../templates/src/builders/web.ts
|
|
31578
|
+
init_src();
|
|
31579
|
+
init_dist();
|
|
31580
|
+
async function buildWebPreset(projectPath, context) {
|
|
31581
|
+
await ensureDirectory(join(projectPath, "src/app"));
|
|
31582
|
+
await ensureDirectory(join(projectPath, "public"));
|
|
31583
|
+
const layoutContent = `import type { Metadata } from 'next'
|
|
31584
|
+
import './globals.css'
|
|
31456
31585
|
|
|
31457
31586
|
export const metadata: Metadata = {
|
|
31458
|
-
title: '${context.projectName}
|
|
31459
|
-
description: '
|
|
31460
|
-
}
|
|
31587
|
+
title: '${context.projectName}',
|
|
31588
|
+
description: 'Built with bunkit \uD83C\uDF5E',
|
|
31589
|
+
}
|
|
31461
31590
|
|
|
31462
31591
|
export default function RootLayout({
|
|
31463
31592
|
children,
|
|
31464
31593
|
}: {
|
|
31465
|
-
children: React.ReactNode
|
|
31594
|
+
children: React.ReactNode
|
|
31466
31595
|
}) {
|
|
31467
31596
|
return (
|
|
31468
31597
|
<html lang="en">
|
|
31469
|
-
<body
|
|
31598
|
+
<body>{children}</body>
|
|
31470
31599
|
</html>
|
|
31471
|
-
)
|
|
31600
|
+
)
|
|
31472
31601
|
}
|
|
31473
|
-
|
|
31474
|
-
await writeFile(join(projectPath, "
|
|
31475
|
-
|
|
31476
|
-
export default function DashboardPage() {
|
|
31602
|
+
`;
|
|
31603
|
+
await writeFile(join(projectPath, "src/app/layout.tsx"), layoutContent);
|
|
31604
|
+
const pageContent = `export default function Home() {
|
|
31477
31605
|
return (
|
|
31478
|
-
<main className="min-h-screen
|
|
31479
|
-
<div className="
|
|
31480
|
-
<
|
|
31481
|
-
|
|
31482
|
-
|
|
31483
|
-
|
|
31484
|
-
|
|
31485
|
-
|
|
31486
|
-
</p>
|
|
31487
|
-
</div>
|
|
31488
|
-
</header>
|
|
31489
|
-
|
|
31490
|
-
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
31491
|
-
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31492
|
-
<div className="flex items-center gap-3 mb-2">
|
|
31493
|
-
<User className="w-5 h-5 text-blue-500" />
|
|
31494
|
-
<h2 className="text-lg font-semibold">Users</h2>
|
|
31495
|
-
</div>
|
|
31496
|
-
<p className="text-3xl font-bold">1,234</p>
|
|
31497
|
-
</div>
|
|
31498
|
-
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31499
|
-
<div className="flex items-center gap-3 mb-2">
|
|
31500
|
-
<Dollar className="w-5 h-5 text-green-500" />
|
|
31501
|
-
<h2 className="text-lg font-semibold">Revenue</h2>
|
|
31502
|
-
</div>
|
|
31503
|
-
<p className="text-3xl font-bold">$12,345</p>
|
|
31504
|
-
</div>
|
|
31505
|
-
<div className="bg-card p-6 rounded-lg border shadow-sm">
|
|
31506
|
-
<div className="flex items-center gap-3 mb-2">
|
|
31507
|
-
<GraphUp className="w-5 h-5 text-purple-500" />
|
|
31508
|
-
<h2 className="text-lg font-semibold">Active</h2>
|
|
31509
|
-
</div>
|
|
31510
|
-
<p className="text-3xl font-bold">567</p>
|
|
31511
|
-
</div>
|
|
31512
|
-
</div>
|
|
31606
|
+
<main className="min-h-screen flex items-center justify-center">
|
|
31607
|
+
<div className="text-center">
|
|
31608
|
+
<h1 className="text-4xl font-bold mb-4">
|
|
31609
|
+
Welcome to ${context.projectName} \uD83C\uDF5E
|
|
31610
|
+
</h1>
|
|
31611
|
+
<p className="text-gray-600">
|
|
31612
|
+
Built with Next.js 16, React 19, and bunkit
|
|
31613
|
+
</p>
|
|
31513
31614
|
</div>
|
|
31514
31615
|
</main>
|
|
31515
|
-
)
|
|
31516
|
-
}
|
|
31517
|
-
`);
|
|
31518
|
-
await writeFile(join(projectPath, "apps/platform/next.config.ts"), `import type { NextConfig } from 'next';
|
|
31519
|
-
|
|
31520
|
-
const nextConfig: NextConfig = {
|
|
31521
|
-
transpilePackages: ['@${scopeName}/ui'],
|
|
31522
|
-
};
|
|
31523
|
-
|
|
31524
|
-
export default nextConfig;
|
|
31525
|
-
`);
|
|
31526
|
-
await writeFile(join(projectPath, "apps/platform/tsconfig.json"), JSON.stringify({
|
|
31527
|
-
extends: "../../tooling/typescript/nextjs.json",
|
|
31528
|
-
compilerOptions: {
|
|
31529
|
-
paths: {
|
|
31530
|
-
"@/*": ["./src/*"]
|
|
31531
|
-
}
|
|
31532
|
-
},
|
|
31533
|
-
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
31534
|
-
exclude: ["node_modules"]
|
|
31535
|
-
}, null, 2));
|
|
31536
|
-
await writeFile(join(projectPath, "apps/platform/postcss.config.mjs"), `export default {
|
|
31537
|
-
plugins: {
|
|
31538
|
-
'@tailwindcss/postcss': {},
|
|
31539
|
-
},
|
|
31540
|
-
};
|
|
31541
|
-
`);
|
|
31542
|
-
await writeHonoApiPackageJson(join(projectPath, "apps/api"), scopeName, {
|
|
31543
|
-
usesTypes: true
|
|
31544
|
-
});
|
|
31545
|
-
await writeFile(join(projectPath, "apps/api/src/index.ts"), `import { Hono } from 'hono';
|
|
31546
|
-
import { logger } from 'hono/logger';
|
|
31547
|
-
import { cors } from 'hono/cors';
|
|
31548
|
-
|
|
31549
|
-
const app = new Hono();
|
|
31550
|
-
|
|
31551
|
-
// Middleware
|
|
31552
|
-
app.use('*', logger());
|
|
31553
|
-
app.use('*', cors());
|
|
31554
|
-
|
|
31555
|
-
// Routes
|
|
31556
|
-
app.get('/', (context) => {
|
|
31557
|
-
return context.json({
|
|
31558
|
-
name: '${context.projectName} API',
|
|
31559
|
-
version: '1.0.0',
|
|
31560
|
-
timestamp: new Date().toISOString(),
|
|
31561
|
-
});
|
|
31562
|
-
});
|
|
31563
|
-
|
|
31564
|
-
app.get('/health', (context) => {
|
|
31565
|
-
return context.json({ status: 'ok' });
|
|
31566
|
-
});
|
|
31567
|
-
|
|
31568
|
-
app.get('/api/users', (context) => {
|
|
31569
|
-
return context.json({
|
|
31570
|
-
users: [
|
|
31571
|
-
{ id: '1', email: 'john@example.com', name: 'John Doe' },
|
|
31572
|
-
{ id: '2', email: 'jane@example.com', name: 'Jane Smith' },
|
|
31573
|
-
],
|
|
31574
|
-
});
|
|
31575
|
-
});
|
|
31616
|
+
)
|
|
31617
|
+
}
|
|
31576
31618
|
|
|
31577
|
-
//
|
|
31578
|
-
|
|
31579
|
-
|
|
31580
|
-
}
|
|
31619
|
+
// Next.js 16 Note:
|
|
31620
|
+
// When you add dynamic routes with params, make your component async and await params:
|
|
31621
|
+
// export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|
31622
|
+
// const { slug } = await params;
|
|
31623
|
+
// return <div>{slug}</div>;
|
|
31624
|
+
// }
|
|
31625
|
+
`;
|
|
31626
|
+
await writeFile(join(projectPath, "src/app/page.tsx"), pageContent);
|
|
31627
|
+
const globalsCssContent = `@import "tailwindcss";
|
|
31628
|
+
`;
|
|
31629
|
+
await writeFile(join(projectPath, "src/app/globals.css"), globalsCssContent);
|
|
31630
|
+
const nextConfigContent = `import type { NextConfig } from 'next';
|
|
31581
31631
|
|
|
31582
|
-
|
|
31583
|
-
|
|
31584
|
-
|
|
31585
|
-
return context.json({ error: 'Internal server error' }, 500);
|
|
31586
|
-
});
|
|
31632
|
+
const nextConfig: NextConfig = {
|
|
31633
|
+
/* config options here */
|
|
31634
|
+
};
|
|
31587
31635
|
|
|
31588
|
-
|
|
31589
|
-
|
|
31590
|
-
|
|
31591
|
-
|
|
31592
|
-
development: {
|
|
31593
|
-
hmr: true,
|
|
31594
|
-
},
|
|
31595
|
-
});
|
|
31636
|
+
export default nextConfig;
|
|
31637
|
+
`;
|
|
31638
|
+
await writeFile(join(projectPath, "next.config.ts"), nextConfigContent);
|
|
31639
|
+
const tailwindConfigContent = `import type { Config } from 'tailwindcss';
|
|
31596
31640
|
|
|
31597
|
-
|
|
31641
|
+
const config: Config = {
|
|
31642
|
+
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
|
31643
|
+
theme: {
|
|
31644
|
+
extend: {},
|
|
31645
|
+
},
|
|
31646
|
+
plugins: [],
|
|
31647
|
+
};
|
|
31598
31648
|
|
|
31599
|
-
export default
|
|
31600
|
-
|
|
31601
|
-
await writeFile(join(projectPath, "
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31606
|
-
|
|
31649
|
+
export default config;
|
|
31650
|
+
`;
|
|
31651
|
+
await writeFile(join(projectPath, "tailwind.config.ts"), tailwindConfigContent);
|
|
31652
|
+
const getTsCompilerOptions = () => {
|
|
31653
|
+
const baseOptions = {
|
|
31654
|
+
target: "ES2017",
|
|
31655
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
31656
|
+
allowJs: true,
|
|
31657
|
+
skipLibCheck: true,
|
|
31658
|
+
noEmit: true,
|
|
31659
|
+
esModuleInterop: true,
|
|
31660
|
+
module: "esnext",
|
|
31661
|
+
moduleResolution: "bundler",
|
|
31662
|
+
resolveJsonModule: true,
|
|
31663
|
+
isolatedModules: true,
|
|
31664
|
+
jsx: "react-jsx",
|
|
31665
|
+
incremental: true,
|
|
31666
|
+
plugins: [{ name: "next" }],
|
|
31667
|
+
paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
|
|
31668
|
+
};
|
|
31669
|
+
if (context.tsStrictness === "strict") {
|
|
31670
|
+
return {
|
|
31671
|
+
...baseOptions,
|
|
31672
|
+
strict: true,
|
|
31673
|
+
noUnusedLocals: true,
|
|
31674
|
+
noUnusedParameters: true,
|
|
31675
|
+
noFallthroughCasesInSwitch: true,
|
|
31676
|
+
noImplicitReturns: true
|
|
31677
|
+
};
|
|
31678
|
+
}
|
|
31679
|
+
if (context.tsStrictness === "moderate") {
|
|
31680
|
+
return {
|
|
31681
|
+
...baseOptions,
|
|
31682
|
+
strict: true,
|
|
31683
|
+
noUnusedLocals: false,
|
|
31684
|
+
noUnusedParameters: false
|
|
31685
|
+
};
|
|
31686
|
+
}
|
|
31687
|
+
return {
|
|
31688
|
+
...baseOptions,
|
|
31689
|
+
strict: false,
|
|
31690
|
+
noImplicitAny: false
|
|
31691
|
+
};
|
|
31692
|
+
};
|
|
31693
|
+
const tsconfigContent = {
|
|
31694
|
+
compilerOptions: getTsCompilerOptions(),
|
|
31695
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
|
|
31607
31696
|
exclude: ["node_modules"]
|
|
31608
|
-
}
|
|
31609
|
-
await
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
shadcnBase: context.shadcnBase || "radix",
|
|
31613
|
-
shadcnBaseColor: context.shadcnBaseColor || "zinc",
|
|
31614
|
-
shadcnIconLibrary: context.shadcnIconLibrary || "phosphor",
|
|
31615
|
-
shadcnMenuAccent: context.shadcnMenuAccent || "subtle",
|
|
31616
|
-
shadcnMenuColor: context.shadcnMenuColor || "default",
|
|
31617
|
-
shadcnRadius: context.shadcnRadius || "0.625rem",
|
|
31618
|
-
appsToScan: ["web", "platform"]
|
|
31619
|
-
});
|
|
31620
|
-
await buildTypesPackage(join(projectPath, "packages"), scopeName);
|
|
31621
|
-
await buildUtilsPackage(join(projectPath, "packages"), scopeName);
|
|
31622
|
-
await setupTooling(projectPath, context);
|
|
31623
|
-
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify({
|
|
31624
|
-
extends: "./tooling/typescript/base.json",
|
|
31625
|
-
include: ["apps/*/src/**/*", "packages/*/src/**/*"],
|
|
31626
|
-
exclude: ["node_modules", "**/node_modules", "**/.next"]
|
|
31627
|
-
}, null, 2));
|
|
31697
|
+
};
|
|
31698
|
+
await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
|
|
31699
|
+
const bunfigContent = generateBunfigContent(context);
|
|
31700
|
+
await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
|
|
31628
31701
|
if (context.codeQuality === "ultracite") {
|
|
31629
31702
|
await setupUltracite(projectPath, context);
|
|
31630
31703
|
} else {
|
|
@@ -31636,13 +31709,52 @@ export default app;
|
|
|
31636
31709
|
if (context.cicd) {
|
|
31637
31710
|
await setupGitHubActions(projectPath, context);
|
|
31638
31711
|
}
|
|
31639
|
-
|
|
31640
|
-
|
|
31712
|
+
if (context.uiLibrary === "shadcn" && context.cssFramework === "tailwind") {
|
|
31713
|
+
await setupShadcnWeb(projectPath, context);
|
|
31714
|
+
}
|
|
31715
|
+
await setupVSCodeDebug(projectPath, context, "web");
|
|
31716
|
+
const packageJsonPath = join(projectPath, "package.json");
|
|
31717
|
+
const existingPackageJson = JSON.parse(await Bun.file(packageJsonPath).text());
|
|
31718
|
+
existingPackageJson.dependencies = {
|
|
31719
|
+
react: "^19.2.3",
|
|
31720
|
+
"react-dom": "^19.2.3",
|
|
31721
|
+
next: "^16.0.10",
|
|
31722
|
+
...existingPackageJson.dependencies
|
|
31723
|
+
};
|
|
31724
|
+
existingPackageJson.devDependencies = {
|
|
31725
|
+
...existingPackageJson.devDependencies,
|
|
31726
|
+
"@types/react": "^19.2.7",
|
|
31727
|
+
"@types/react-dom": "^19.2.3",
|
|
31728
|
+
"@types/node": "^25.0.3"
|
|
31729
|
+
};
|
|
31730
|
+
if (!existingPackageJson.scripts) {
|
|
31731
|
+
existingPackageJson.scripts = {};
|
|
31732
|
+
}
|
|
31733
|
+
if (!existingPackageJson.scripts.dev) {
|
|
31734
|
+
existingPackageJson.scripts.dev = "bun run --bun next dev --turbopack";
|
|
31735
|
+
}
|
|
31736
|
+
if (!existingPackageJson.scripts.build) {
|
|
31737
|
+
existingPackageJson.scripts.build = "bun run --bun next build";
|
|
31738
|
+
}
|
|
31739
|
+
if (!existingPackageJson.scripts.start) {
|
|
31740
|
+
existingPackageJson.scripts.start = "bun run --bun next start";
|
|
31741
|
+
}
|
|
31742
|
+
if (!existingPackageJson.scripts.debug) {
|
|
31743
|
+
existingPackageJson.scripts.debug = "bun --inspect node_modules/.bin/next dev --turbopack";
|
|
31744
|
+
}
|
|
31745
|
+
if (!existingPackageJson.scripts["debug:brk"]) {
|
|
31746
|
+
existingPackageJson.scripts["debug:brk"] = "bun --inspect-brk node_modules/.bin/next dev --turbopack";
|
|
31747
|
+
}
|
|
31748
|
+
if (!existingPackageJson.scripts["debug:wait"]) {
|
|
31749
|
+
existingPackageJson.scripts["debug:wait"] = "bun --inspect-wait node_modules/.bin/next dev --turbopack";
|
|
31750
|
+
}
|
|
31751
|
+
await writeFile(packageJsonPath, JSON.stringify(existingPackageJson, null, 2));
|
|
31752
|
+
await generateNextjsReadme(projectPath, context);
|
|
31641
31753
|
}
|
|
31642
|
-
// ../templates/src/shared/index.ts
|
|
31643
|
-
init_package_json();
|
|
31644
31754
|
// ../templates/src/render.ts
|
|
31645
31755
|
var import_ejs = __toESM(require_ejs(), 1);
|
|
31756
|
+
// ../templates/src/shared/index.ts
|
|
31757
|
+
init_package_json();
|
|
31646
31758
|
// src/commands/add/component.ts
|
|
31647
31759
|
init_dist();
|
|
31648
31760
|
async function getPackageName(cwd2) {
|
|
@@ -33113,29 +33225,54 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33113
33225
|
options: [
|
|
33114
33226
|
{
|
|
33115
33227
|
value: "radix-maia",
|
|
33116
|
-
label: "Maia (Recommended)",
|
|
33228
|
+
label: "Maia \u2014 Radix (Recommended)",
|
|
33117
33229
|
hint: "Modern, clean design with soft shadows and refined aesthetics"
|
|
33118
33230
|
},
|
|
33119
33231
|
{
|
|
33120
33232
|
value: "radix-vega",
|
|
33121
|
-
label: "Vega",
|
|
33233
|
+
label: "Vega \u2014 Radix",
|
|
33122
33234
|
hint: "Bold, vibrant design with stronger colors and contrast"
|
|
33123
33235
|
},
|
|
33124
33236
|
{
|
|
33125
33237
|
value: "radix-nova",
|
|
33126
|
-
label: "Nova",
|
|
33238
|
+
label: "Nova \u2014 Radix",
|
|
33127
33239
|
hint: "Minimalist design with subtle accents and clean lines"
|
|
33128
33240
|
},
|
|
33129
33241
|
{
|
|
33130
33242
|
value: "radix-lyra",
|
|
33131
|
-
label: "Lyra",
|
|
33243
|
+
label: "Lyra \u2014 Radix",
|
|
33132
33244
|
hint: "Elegant design with refined typography and spacing"
|
|
33133
33245
|
},
|
|
33134
33246
|
{
|
|
33135
33247
|
value: "radix-mira",
|
|
33136
|
-
label: "Mira",
|
|
33248
|
+
label: "Mira \u2014 Radix",
|
|
33137
33249
|
hint: "Playful design with rounded elements and soft colors"
|
|
33138
33250
|
},
|
|
33251
|
+
{
|
|
33252
|
+
value: "base-maia",
|
|
33253
|
+
label: "Maia \u2014 Base UI",
|
|
33254
|
+
hint: "Clean design using Base UI primitives (alternative to Radix)"
|
|
33255
|
+
},
|
|
33256
|
+
{
|
|
33257
|
+
value: "base-vega",
|
|
33258
|
+
label: "Vega \u2014 Base UI",
|
|
33259
|
+
hint: "Bold design using Base UI primitives"
|
|
33260
|
+
},
|
|
33261
|
+
{
|
|
33262
|
+
value: "base-nova",
|
|
33263
|
+
label: "Nova \u2014 Base UI",
|
|
33264
|
+
hint: "Minimalist design using Base UI primitives"
|
|
33265
|
+
},
|
|
33266
|
+
{
|
|
33267
|
+
value: "base-lyra",
|
|
33268
|
+
label: "Lyra \u2014 Base UI",
|
|
33269
|
+
hint: "Elegant design using Base UI primitives"
|
|
33270
|
+
},
|
|
33271
|
+
{
|
|
33272
|
+
value: "base-mira",
|
|
33273
|
+
label: "Mira \u2014 Base UI",
|
|
33274
|
+
hint: "Playful design using Base UI primitives"
|
|
33275
|
+
},
|
|
33139
33276
|
{
|
|
33140
33277
|
value: "new-york",
|
|
33141
33278
|
label: "New York (Legacy)",
|
|
@@ -33223,21 +33360,21 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33223
33360
|
}
|
|
33224
33361
|
}
|
|
33225
33362
|
let shadcnIconLibrary = getOptionValue("BUNKIT_SHADCN_ICON_LIBRARY", options.shadcnIconLibrary);
|
|
33226
|
-
const
|
|
33363
|
+
const isModernStyle = shadcnStyle?.startsWith("radix-") || shadcnStyle?.startsWith("base-");
|
|
33227
33364
|
if (!shadcnIconLibrary && uiLibrary === "shadcn") {
|
|
33228
33365
|
if (!isNonInteractive) {
|
|
33229
33366
|
shadcnIconLibrary = await ve({
|
|
33230
33367
|
message: "\uD83D\uDD23 Icon library",
|
|
33231
33368
|
options: [
|
|
33232
33369
|
{
|
|
33233
|
-
value: "
|
|
33234
|
-
label:
|
|
33235
|
-
hint: "
|
|
33370
|
+
value: "iconoir",
|
|
33371
|
+
label: "Iconoir (Recommended)",
|
|
33372
|
+
hint: "bunkit default - 1600+ lightweight, tree-shakeable icons"
|
|
33236
33373
|
},
|
|
33237
33374
|
{
|
|
33238
|
-
value: "
|
|
33239
|
-
label:
|
|
33240
|
-
hint: "
|
|
33375
|
+
value: "phosphor",
|
|
33376
|
+
label: "Phosphor Icons",
|
|
33377
|
+
hint: "Modern icon library with consistent design - shadcn/ui default for new styles"
|
|
33241
33378
|
},
|
|
33242
33379
|
{
|
|
33243
33380
|
value: "lucide",
|
|
@@ -33251,39 +33388,19 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33251
33388
|
process.exit(0);
|
|
33252
33389
|
}
|
|
33253
33390
|
} else {
|
|
33254
|
-
shadcnIconLibrary =
|
|
33391
|
+
shadcnIconLibrary = "iconoir";
|
|
33255
33392
|
}
|
|
33256
33393
|
}
|
|
33257
33394
|
let shadcnBase = getOptionValue("BUNKIT_SHADCN_BASE", options.shadcnBase);
|
|
33258
|
-
if (!shadcnBase && uiLibrary === "shadcn"
|
|
33259
|
-
if (
|
|
33260
|
-
shadcnBase =
|
|
33261
|
-
message: "\uD83E\uDDF1 Component foundation",
|
|
33262
|
-
options: [
|
|
33263
|
-
{
|
|
33264
|
-
value: "radix",
|
|
33265
|
-
label: "Radix UI (Recommended)",
|
|
33266
|
-
hint: "Battle-tested accessibility primitives - industry standard"
|
|
33267
|
-
},
|
|
33268
|
-
{
|
|
33269
|
-
value: "base-ui",
|
|
33270
|
-
label: "Base UI",
|
|
33271
|
-
hint: "Modern alternative with similar API - newer, experimental"
|
|
33272
|
-
}
|
|
33273
|
-
]
|
|
33274
|
-
});
|
|
33275
|
-
if (pD(shadcnBase)) {
|
|
33276
|
-
xe("Operation cancelled.");
|
|
33277
|
-
process.exit(0);
|
|
33278
|
-
}
|
|
33395
|
+
if (!shadcnBase && uiLibrary === "shadcn") {
|
|
33396
|
+
if (shadcnStyle?.startsWith("base-")) {
|
|
33397
|
+
shadcnBase = "base-ui";
|
|
33279
33398
|
} else {
|
|
33280
33399
|
shadcnBase = "radix";
|
|
33281
33400
|
}
|
|
33282
|
-
} else if (!shadcnBase) {
|
|
33283
|
-
shadcnBase = "radix";
|
|
33284
33401
|
}
|
|
33285
33402
|
let shadcnMenuAccent = getOptionValue("BUNKIT_SHADCN_MENU_ACCENT", options.shadcnMenuAccent);
|
|
33286
|
-
if (!shadcnMenuAccent && uiLibrary === "shadcn" &&
|
|
33403
|
+
if (!shadcnMenuAccent && uiLibrary === "shadcn" && isModernStyle) {
|
|
33287
33404
|
if (!isNonInteractive) {
|
|
33288
33405
|
shadcnMenuAccent = await ve({
|
|
33289
33406
|
message: "\u2728 Menu accent style",
|
|
@@ -33311,7 +33428,7 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33311
33428
|
shadcnMenuAccent = "subtle";
|
|
33312
33429
|
}
|
|
33313
33430
|
let shadcnMenuColor = getOptionValue("BUNKIT_SHADCN_MENU_COLOR", options.shadcnMenuColor);
|
|
33314
|
-
if (!shadcnMenuColor && uiLibrary === "shadcn" &&
|
|
33431
|
+
if (!shadcnMenuColor && uiLibrary === "shadcn" && isModernStyle) {
|
|
33315
33432
|
if (!isNonInteractive) {
|
|
33316
33433
|
shadcnMenuColor = await ve({
|
|
33317
33434
|
message: "\uD83C\uDFAF Menu color scheme",
|
|
@@ -33338,6 +33455,21 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33338
33455
|
} else if (!shadcnMenuColor) {
|
|
33339
33456
|
shadcnMenuColor = "default";
|
|
33340
33457
|
}
|
|
33458
|
+
let shadcnRtl = getOptionValue("BUNKIT_SHADCN_RTL", options.shadcnRtl, false);
|
|
33459
|
+
if (shadcnRtl === undefined && uiLibrary === "shadcn" && isModernStyle) {
|
|
33460
|
+
if (!isNonInteractive) {
|
|
33461
|
+
shadcnRtl = await ye({
|
|
33462
|
+
message: "\uD83D\uDD04 Enable RTL (right-to-left) support",
|
|
33463
|
+
initialValue: false
|
|
33464
|
+
});
|
|
33465
|
+
if (pD(shadcnRtl)) {
|
|
33466
|
+
xe("Operation cancelled.");
|
|
33467
|
+
process.exit(0);
|
|
33468
|
+
}
|
|
33469
|
+
} else {
|
|
33470
|
+
shadcnRtl = false;
|
|
33471
|
+
}
|
|
33472
|
+
}
|
|
33341
33473
|
let testing = getOptionValue("BUNKIT_TESTING", options.testing, "bun-test");
|
|
33342
33474
|
if (!testing) {
|
|
33343
33475
|
if (!isNonInteractive) {
|
|
@@ -33584,6 +33716,7 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33584
33716
|
shadcnMenuAccent,
|
|
33585
33717
|
shadcnMenuColor,
|
|
33586
33718
|
shadcnRadius,
|
|
33719
|
+
shadcnRtl: shadcnRtl || false,
|
|
33587
33720
|
supabasePreset,
|
|
33588
33721
|
supabaseFeatures,
|
|
33589
33722
|
supabaseWithDrizzle
|
|
@@ -33793,6 +33926,130 @@ async function enhancedInitCommand(options = {}) {
|
|
|
33793
33926
|
}
|
|
33794
33927
|
}
|
|
33795
33928
|
|
|
33929
|
+
// src/commands/migrate.ts
|
|
33930
|
+
import { existsSync as existsSync3 } from "fs";
|
|
33931
|
+
init_source();
|
|
33932
|
+
init_dist();
|
|
33933
|
+
var VALID_MIGRATIONS = ["radix", "rtl", "icons"];
|
|
33934
|
+
var MIGRATION_DESCRIPTIONS = {
|
|
33935
|
+
radix: "Migrate to unified radix-ui package (replaces @radix-ui/react-* packages)",
|
|
33936
|
+
rtl: "Enable RTL (right-to-left) support in shadcn/ui components",
|
|
33937
|
+
icons: "Migrate icon library (e.g., lucide to iconoir or phosphor)"
|
|
33938
|
+
};
|
|
33939
|
+
function detectShadcnRoot(projectPath) {
|
|
33940
|
+
const monorepoPath = join(projectPath, "packages/ui");
|
|
33941
|
+
if (existsSync3(join(monorepoPath, "components.json"))) {
|
|
33942
|
+
return monorepoPath;
|
|
33943
|
+
}
|
|
33944
|
+
if (existsSync3(join(projectPath, "components.json"))) {
|
|
33945
|
+
return projectPath;
|
|
33946
|
+
}
|
|
33947
|
+
return null;
|
|
33948
|
+
}
|
|
33949
|
+
async function runBunInstall(projectPath) {
|
|
33950
|
+
const installSpinner = Y2();
|
|
33951
|
+
installSpinner.start("Resolving dependencies (bun install)...");
|
|
33952
|
+
try {
|
|
33953
|
+
const installProcess = Bun.spawn(["bun", "install"], {
|
|
33954
|
+
cwd: projectPath,
|
|
33955
|
+
stdout: "pipe",
|
|
33956
|
+
stderr: "pipe"
|
|
33957
|
+
});
|
|
33958
|
+
await installProcess.exited;
|
|
33959
|
+
if (installProcess.exitCode !== 0) {
|
|
33960
|
+
const stderr = await new Response(installProcess.stderr).text();
|
|
33961
|
+
installSpinner.stop(source_default.red("Failed to install dependencies"));
|
|
33962
|
+
Me(stderr, "Error Details");
|
|
33963
|
+
return false;
|
|
33964
|
+
}
|
|
33965
|
+
installSpinner.stop(source_default.green("Dependencies resolved"));
|
|
33966
|
+
return true;
|
|
33967
|
+
} catch (error) {
|
|
33968
|
+
installSpinner.stop(source_default.red("Failed to install dependencies"));
|
|
33969
|
+
Me(String(error), "Error Details");
|
|
33970
|
+
return false;
|
|
33971
|
+
}
|
|
33972
|
+
}
|
|
33973
|
+
async function runShadcnMigrate(shadcnRoot, migrationType) {
|
|
33974
|
+
const migrateSpinner = Y2();
|
|
33975
|
+
migrateSpinner.start(`Running shadcn migrate ${migrationType}...`);
|
|
33976
|
+
try {
|
|
33977
|
+
const migrateProcess = Bun.spawn(["bunx", "shadcn@latest", "migrate", migrationType], {
|
|
33978
|
+
cwd: shadcnRoot,
|
|
33979
|
+
stdout: "pipe",
|
|
33980
|
+
stderr: "pipe"
|
|
33981
|
+
});
|
|
33982
|
+
await migrateProcess.exited;
|
|
33983
|
+
if (migrateProcess.exitCode !== 0) {
|
|
33984
|
+
const stderr = await new Response(migrateProcess.stderr).text();
|
|
33985
|
+
migrateSpinner.stop(source_default.red(`Migration failed: ${migrationType}`));
|
|
33986
|
+
Me(stderr, "Error Details");
|
|
33987
|
+
return false;
|
|
33988
|
+
}
|
|
33989
|
+
migrateSpinner.stop(source_default.green(`Migration complete: ${migrationType}`));
|
|
33990
|
+
return true;
|
|
33991
|
+
} catch (error) {
|
|
33992
|
+
migrateSpinner.stop(source_default.red(`Migration failed: ${migrationType}`));
|
|
33993
|
+
Me(String(error), "Error Details");
|
|
33994
|
+
return false;
|
|
33995
|
+
}
|
|
33996
|
+
}
|
|
33997
|
+
function showPostMigrationAdvice(migrationType, isMonorepo) {
|
|
33998
|
+
const advice = [];
|
|
33999
|
+
switch (migrationType) {
|
|
34000
|
+
case "radix":
|
|
34001
|
+
advice.push("The unified radix-ui package replaces individual @radix-ui/react-* packages.", "Old packages can be removed from your package.json.", isMonorepo ? "Check packages/ui/package.json and root catalog for cleanup." : "Check package.json for cleanup.", "", "Run: bun install (to update lockfile)");
|
|
34002
|
+
break;
|
|
34003
|
+
case "rtl":
|
|
34004
|
+
advice.push("RTL support has been added to your components.", "Class names have been transformed for RTL compatibility.", 'components.json now includes "rtl": true.', "", 'Set dir="rtl" on your <html> element to activate RTL layout.');
|
|
34005
|
+
break;
|
|
34006
|
+
case "icons":
|
|
34007
|
+
advice.push("Icon imports have been updated in your components.", "Check components.json to verify the iconLibrary field.", "", "You may need to install the new icon package:", " bun add iconoir-react (bunkit default)", " bun add @phosphor-icons/react", " bun add lucide-react");
|
|
34008
|
+
break;
|
|
34009
|
+
}
|
|
34010
|
+
if (advice.length > 0) {
|
|
34011
|
+
Me(advice.join(`
|
|
34012
|
+
`), "Post-Migration");
|
|
34013
|
+
}
|
|
34014
|
+
}
|
|
34015
|
+
async function migrateCommand(migrationType) {
|
|
34016
|
+
if (!migrationType) {
|
|
34017
|
+
migrationType = await ve({
|
|
34018
|
+
message: "Select migration type",
|
|
34019
|
+
options: VALID_MIGRATIONS.map((migration2) => ({
|
|
34020
|
+
value: migration2,
|
|
34021
|
+
label: migration2,
|
|
34022
|
+
hint: MIGRATION_DESCRIPTIONS[migration2]
|
|
34023
|
+
}))
|
|
34024
|
+
});
|
|
34025
|
+
if (pD(migrationType)) {
|
|
34026
|
+
xe("Operation cancelled.");
|
|
34027
|
+
process.exit(0);
|
|
34028
|
+
}
|
|
34029
|
+
}
|
|
34030
|
+
if (!VALID_MIGRATIONS.includes(migrationType)) {
|
|
34031
|
+
throw new Error(`Invalid migration type: "${migrationType}". ` + `Valid types: ${VALID_MIGRATIONS.join(", ")}`);
|
|
34032
|
+
}
|
|
34033
|
+
const migration = migrationType;
|
|
34034
|
+
const projectPath = process.cwd();
|
|
34035
|
+
const shadcnRoot = detectShadcnRoot(projectPath);
|
|
34036
|
+
if (!shadcnRoot) {
|
|
34037
|
+
throw new Error("Could not find components.json. " + "Run this command from a project with shadcn/ui configured, " + "or from a monorepo root with packages/ui/components.json.");
|
|
34038
|
+
}
|
|
34039
|
+
const isMonorepo = shadcnRoot !== projectPath;
|
|
34040
|
+
M2.info(`${source_default.bold("Migration:")} ${migration} \u2014 ${MIGRATION_DESCRIPTIONS[migration]}`);
|
|
34041
|
+
M2.info(`${source_default.bold("Location:")} ${isMonorepo ? "packages/ui (monorepo)" : "(project root)"}`);
|
|
34042
|
+
const installSuccess = await runBunInstall(projectPath);
|
|
34043
|
+
if (!installSuccess) {
|
|
34044
|
+
throw new Error("Failed to resolve dependencies. Fix the errors above and try again.");
|
|
34045
|
+
}
|
|
34046
|
+
const migrateSuccess = await runShadcnMigrate(shadcnRoot, migration);
|
|
34047
|
+
if (!migrateSuccess) {
|
|
34048
|
+
throw new Error(`Migration "${migration}" failed. Check the errors above.`);
|
|
34049
|
+
}
|
|
34050
|
+
showPostMigrationAdvice(migration, isMonorepo);
|
|
34051
|
+
}
|
|
34052
|
+
|
|
33796
34053
|
// src/commands/preset.ts
|
|
33797
34054
|
init_src();
|
|
33798
34055
|
init_boxen();
|
|
@@ -33906,12 +34163,13 @@ Examples:
|
|
|
33906
34163
|
$ bunkit create nextjs my-app Create a Next.js web application
|
|
33907
34164
|
$ bunkit add workspace Add a new workspace to monorepo
|
|
33908
34165
|
$ bunkit add component --all Browse and add shadcn/ui components
|
|
34166
|
+
$ bunkit migrate radix Migrate to unified radix-ui package
|
|
33909
34167
|
$ bunkit catalog add zod ^3.24.1 Add package to dependency catalog
|
|
33910
34168
|
$ bunkit preset list List all custom presets
|
|
33911
34169
|
|
|
33912
34170
|
For more information, visit: https://github.com/Arakiss/bunkit
|
|
33913
34171
|
`);
|
|
33914
|
-
program2.command("init").description("Create a new project with full customization options").alias("i").option("--name <name>", "Project name (kebab-case recommended, e.g., my-awesome-app)").option("--preset <preset>", "Project preset: minimal | nextjs | hono-api | bun-api | bun-fullstack | nextjs-monorepo | bun-monorepo | enterprise-monorepo").option("--database <database>", "Database option: postgres-drizzle | postgres-prisma | mysql-drizzle | mysql-prisma | supabase | supabase-drizzle | supabase-prisma | sqlite-drizzle | sqlite-prisma | none").option("--auth <auth>", "Authentication system: better-auth | nextauth | supabase | none").option("--redis", "Enable Redis cache/session store").option("--use-bun-secrets", "Use Bun.secrets API instead of .env files").option("--supabase-preset <preset>", "Supabase configuration preset: full-stack | auth-only | database-only | custom").option("--supabase-features <features>", "Comma-separated Supabase features: auth,storage,realtime,edge-functions,database").option("--code-quality <tool>", "Code quality tool: ultracite | biome").option("--ts-strictness <level>", "TypeScript strictness level: strict | moderate | loose").option("--ui-library <library>", "UI component library: shadcn | none").option("--css-framework <framework>", "CSS framework: tailwind | vanilla | css-modules").option("--shadcn-style <style>", "shadcn/ui style: radix-maia | radix-vega | radix-nova | radix-lyra | radix-mira | new-york | default").option("--shadcn-base <base>", "shadcn/ui component foundation: radix | base-ui").option("--shadcn-base-color <color>", "shadcn/ui base color theme: zinc | slate | stone | gray | neutral").option("--shadcn-icon-library <library>", "shadcn/ui icon library: phosphor | lucide | iconoir").option("--shadcn-menu-accent <accent>", "shadcn/ui menu accent style: subtle | bold").option("--shadcn-menu-color <color>", "shadcn/ui menu color: default | muted").option("--shadcn-radius <radius>", "shadcn/ui border radius (CSS value, e.g., 0.5rem, 0.625rem)").option("--testing <framework>", "Testing framework: bun-test | vitest | none").option("--docker", "Include Docker configuration files").option("--cicd", "Include GitHub Actions CI/CD workflow").option("--no-git", "Skip Git repository initialization").option("--no-install", "Skip dependency installation after project creation").option("--non-interactive", "Run in non-interactive mode (requires all options via flags)").option("--save-preset <name>", "Save current configuration as a custom preset").option("--load-preset <name>", "Load configuration from a custom preset").addHelpText("after", `
|
|
34172
|
+
program2.command("init").description("Create a new project with full customization options").alias("i").option("--name <name>", "Project name (kebab-case recommended, e.g., my-awesome-app)").option("--preset <preset>", "Project preset: minimal | nextjs | hono-api | bun-api | bun-fullstack | nextjs-monorepo | bun-monorepo | enterprise-monorepo").option("--database <database>", "Database option: postgres-drizzle | postgres-prisma | mysql-drizzle | mysql-prisma | supabase | supabase-drizzle | supabase-prisma | sqlite-drizzle | sqlite-prisma | none").option("--auth <auth>", "Authentication system: better-auth | nextauth | supabase | none").option("--redis", "Enable Redis cache/session store").option("--use-bun-secrets", "Use Bun.secrets API instead of .env files").option("--supabase-preset <preset>", "Supabase configuration preset: full-stack | auth-only | database-only | custom").option("--supabase-features <features>", "Comma-separated Supabase features: auth,storage,realtime,edge-functions,database").option("--code-quality <tool>", "Code quality tool: ultracite | biome").option("--ts-strictness <level>", "TypeScript strictness level: strict | moderate | loose").option("--ui-library <library>", "UI component library: shadcn | none").option("--css-framework <framework>", "CSS framework: tailwind | vanilla | css-modules").option("--shadcn-style <style>", "shadcn/ui style: radix-maia | radix-vega | radix-nova | radix-lyra | radix-mira | base-maia | base-vega | base-nova | base-lyra | base-mira | new-york | default").option("--shadcn-base <base>", "shadcn/ui component foundation: radix | base-ui").option("--shadcn-base-color <color>", "shadcn/ui base color theme: zinc | slate | stone | gray | neutral").option("--shadcn-icon-library <library>", "shadcn/ui icon library: phosphor | lucide | iconoir").option("--shadcn-menu-accent <accent>", "shadcn/ui menu accent style: subtle | bold").option("--shadcn-menu-color <color>", "shadcn/ui menu color: default | muted").option("--shadcn-radius <radius>", "shadcn/ui border radius (CSS value, e.g., 0.5rem, 0.625rem)").option("--shadcn-rtl", "Enable RTL (right-to-left) support for shadcn/ui components").option("--testing <framework>", "Testing framework: bun-test | vitest | none").option("--docker", "Include Docker configuration files").option("--cicd", "Include GitHub Actions CI/CD workflow").option("--no-git", "Skip Git repository initialization").option("--no-install", "Skip dependency installation after project creation").option("--non-interactive", "Run in non-interactive mode (requires all options via flags)").option("--save-preset <name>", "Save current configuration as a custom preset").option("--load-preset <name>", "Load configuration from a custom preset").addHelpText("after", `
|
|
33915
34173
|
Examples:
|
|
33916
34174
|
$ bunkit init Interactive project creation
|
|
33917
34175
|
$ bunkit init --name my-app --preset nextjs Quick web app creation
|
|
@@ -34031,6 +34289,28 @@ Features:
|
|
|
34031
34289
|
process.exit(1);
|
|
34032
34290
|
}
|
|
34033
34291
|
});
|
|
34292
|
+
program2.command("migrate").argument("[type]", "Migration type: radix | rtl | icons").description("Run shadcn/ui migrations (unified radix-ui, RTL, icon library)").addHelpText("after", `
|
|
34293
|
+
Examples:
|
|
34294
|
+
$ bunkit migrate Interactive migration selection
|
|
34295
|
+
$ bunkit migrate radix Migrate to unified radix-ui package
|
|
34296
|
+
$ bunkit migrate rtl Enable RTL support in components
|
|
34297
|
+
$ bunkit migrate icons Migrate icon library
|
|
34298
|
+
|
|
34299
|
+
Migrations:
|
|
34300
|
+
radix Replace @radix-ui/react-* packages with unified radix-ui
|
|
34301
|
+
rtl Add RTL class transforms to installed components
|
|
34302
|
+
icons Migrate between icon libraries (lucide, iconoir, phosphor)
|
|
34303
|
+
`).action(async (migrationType) => {
|
|
34304
|
+
showBanner(VERSION);
|
|
34305
|
+
try {
|
|
34306
|
+
await migrateCommand(migrationType);
|
|
34307
|
+
Se(import_picocolors7.default.green("Migration completed successfully!"));
|
|
34308
|
+
} catch (error) {
|
|
34309
|
+
M2.error(error.message);
|
|
34310
|
+
Se(import_picocolors7.default.red("Migration failed"));
|
|
34311
|
+
process.exit(1);
|
|
34312
|
+
}
|
|
34313
|
+
});
|
|
34034
34314
|
var catalogCmd = new Command("catalog").alias("cat").description("Manage dependency catalog for version synchronization").addHelpText("after", `
|
|
34035
34315
|
Subcommands:
|
|
34036
34316
|
add <package> [version] Add package to catalog
|