@appsbyme/create-basekit 1.0.3 → 1.0.5

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.cjs CHANGED
@@ -4965,6 +4965,39 @@ var require_prompts3 = __commonJS({
4965
4965
  }
4966
4966
  });
4967
4967
 
4968
+ // src/commands/app/package-paths.ts
4969
+ function getCreateBasekitPackageRoot(fromDir = import_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl))) {
4970
+ let dir = import_path.default.resolve(fromDir);
4971
+ for (let i = 0; i < 8; i += 1) {
4972
+ const pkgPath = import_path.default.join(dir, "package.json");
4973
+ if (import_fs.default.existsSync(pkgPath)) {
4974
+ try {
4975
+ const name = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf8")).name;
4976
+ if (name === "@appsbyme/create-basekit") {
4977
+ return dir;
4978
+ }
4979
+ } catch {
4980
+ }
4981
+ }
4982
+ const parent = import_path.default.dirname(dir);
4983
+ if (parent === dir) break;
4984
+ dir = parent;
4985
+ }
4986
+ throw new Error(
4987
+ `Could not find @appsbyme/create-basekit package root (started at ${fromDir})`
4988
+ );
4989
+ }
4990
+ var import_fs, import_path, import_node_url;
4991
+ var init_package_paths = __esm({
4992
+ "src/commands/app/package-paths.ts"() {
4993
+ "use strict";
4994
+ init_cjs_shims();
4995
+ import_fs = __toESM(require("fs"), 1);
4996
+ import_path = __toESM(require("path"), 1);
4997
+ import_node_url = require("url");
4998
+ }
4999
+ });
5000
+
4968
5001
  // src/commands/web/spinner.ts
4969
5002
  function createSpinner(text) {
4970
5003
  let frame = 0;
@@ -5018,27 +5051,27 @@ function logError(msg) {
5018
5051
  console.error(` \u2717 ${msg}`);
5019
5052
  }
5020
5053
  function exists(absPath) {
5021
- return import_fs.default.existsSync(absPath);
5054
+ return import_fs2.default.existsSync(absPath);
5022
5055
  }
5023
5056
  function copyDir(src, dest) {
5024
- import_fs.default.cpSync(src, dest, { recursive: true });
5057
+ import_fs2.default.cpSync(src, dest, { recursive: true });
5025
5058
  }
5026
5059
  function readText(absPath) {
5027
- return import_fs.default.readFileSync(absPath, "utf-8");
5060
+ return import_fs2.default.readFileSync(absPath, "utf-8");
5028
5061
  }
5029
5062
  function writeText(absPath, content) {
5030
- import_fs.default.writeFileSync(absPath, content);
5063
+ import_fs2.default.writeFileSync(absPath, content);
5031
5064
  }
5032
5065
  function readJson(absPath) {
5033
- return JSON.parse(import_fs.default.readFileSync(absPath, "utf-8"));
5066
+ return JSON.parse(import_fs2.default.readFileSync(absPath, "utf-8"));
5034
5067
  }
5035
5068
  function writeJson(absPath, data) {
5036
- import_fs.default.writeFileSync(absPath, JSON.stringify(data, null, 2) + "\n");
5069
+ import_fs2.default.writeFileSync(absPath, JSON.stringify(data, null, 2) + "\n");
5037
5070
  }
5038
5071
  function safeUnlink(absPath) {
5039
5072
  if (!exists(absPath)) return false;
5040
5073
  try {
5041
- import_fs.default.unlinkSync(absPath);
5074
+ import_fs2.default.unlinkSync(absPath);
5042
5075
  return true;
5043
5076
  } catch {
5044
5077
  return false;
@@ -5047,7 +5080,7 @@ function safeUnlink(absPath) {
5047
5080
  function safeRmDir(absDir) {
5048
5081
  if (!exists(absDir)) return false;
5049
5082
  try {
5050
- import_fs.default.rmSync(absDir, { recursive: true, force: true });
5083
+ import_fs2.default.rmSync(absDir, { recursive: true, force: true });
5051
5084
  return true;
5052
5085
  } catch {
5053
5086
  return false;
@@ -5115,7 +5148,7 @@ function removeMarkedSection(content, marker) {
5115
5148
  return content.replace(regex, "\n");
5116
5149
  }
5117
5150
  function getDestDir(appName) {
5118
- return import_path.default.resolve(process.cwd(), appName);
5151
+ return import_path2.default.resolve(process.cwd(), appName);
5119
5152
  }
5120
5153
  function patchReplace(content, pattern, replacement, description) {
5121
5154
  const result = content.replace(pattern, replacement);
@@ -5124,36 +5157,28 @@ function patchReplace(content, pattern, replacement, description) {
5124
5157
  }
5125
5158
  return result;
5126
5159
  }
5127
- var import_child_process, import_fs, import_path;
5160
+ var import_child_process, import_fs2, import_path2;
5128
5161
  var init_utils = __esm({
5129
5162
  "src/commands/web/utils.ts"() {
5130
5163
  "use strict";
5131
5164
  init_cjs_shims();
5132
5165
  import_child_process = require("child_process");
5133
- import_fs = __toESM(require("fs"), 1);
5134
- import_path = __toESM(require("path"), 1);
5166
+ import_fs2 = __toESM(require("fs"), 1);
5167
+ import_path2 = __toESM(require("path"), 1);
5135
5168
  }
5136
5169
  });
5137
5170
 
5138
5171
  // src/commands/web/scaffold.ts
5139
5172
  function templateDir() {
5140
- const here = import_path2.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
5141
- const candidates = [
5142
- import_path2.default.join(here, "../../../template-web"),
5143
- import_path2.default.join(here, "../template-web")
5144
- ];
5145
- for (const candidate of candidates) {
5146
- const resolved = import_path2.default.resolve(candidate);
5147
- if (import_fs2.default.existsSync(resolved)) return resolved;
5148
- }
5149
- throw new Error(
5150
- `Starter template is missing (searched: ${candidates.map((c) => import_path2.default.resolve(c)).join(", ")})`
5173
+ return import_path3.default.join(
5174
+ getCreateBasekitPackageRoot(import_path3.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl))),
5175
+ "template-web"
5151
5176
  );
5152
5177
  }
5153
5178
  async function scaffold(opts) {
5154
5179
  const { appName, description, destDir, enableTailwind, enablePwa, enableDocker, enableHusky } = opts;
5155
5180
  if (exists(destDir)) {
5156
- throw new Error(`Directory "${import_path2.default.basename(destDir)}" already exists.`);
5181
+ throw new Error(`Directory "${import_path3.default.basename(destDir)}" already exists.`);
5157
5182
  }
5158
5183
  try {
5159
5184
  await scaffoldInner(opts);
@@ -5170,10 +5195,11 @@ async function scaffoldInner(opts) {
5170
5195
  const { appName, description, destDir, enableTailwind, enablePwa, enableDocker, enableHusky, portOffset } = opts;
5171
5196
  await cloneTemplate(destDir);
5172
5197
  materializeGitignore(destDir);
5173
- safeUnlink(import_path2.default.join(destDir, "yarn.lock"));
5174
- safeRmDir(import_path2.default.join(destDir, ".git"));
5198
+ materializeYarnrc(destDir);
5199
+ safeUnlink(import_path3.default.join(destDir, "yarn.lock"));
5200
+ safeRmDir(import_path3.default.join(destDir, ".git"));
5175
5201
  logOk("Removed .git history \u2014 clean slate");
5176
- safeUnlink(import_path2.default.join(destDir, "commitlint.config.js"));
5202
+ safeUnlink(import_path3.default.join(destDir, "commitlint.config.js"));
5177
5203
  cleanupGitignore(destDir);
5178
5204
  logStep("Customising template");
5179
5205
  replaceTokens(destDir, appName, description);
@@ -5212,7 +5238,7 @@ async function scaffoldInner(opts) {
5212
5238
  }
5213
5239
  async function cloneTemplate(destDir) {
5214
5240
  const source = templateDir();
5215
- if (!import_fs2.default.existsSync(source)) {
5241
+ if (!import_fs3.default.existsSync(source)) {
5216
5242
  throw new Error(`Starter template is missing at ${source}`);
5217
5243
  }
5218
5244
  const spinner = createSpinner("Copying starter template\u2026");
@@ -5225,14 +5251,19 @@ async function cloneTemplate(destDir) {
5225
5251
  spinner.succeed("Template copied");
5226
5252
  }
5227
5253
  function materializeGitignore(destDir) {
5228
- const packagedPath = import_path2.default.join(destDir, "_gitignore");
5254
+ const packagedPath = import_path3.default.join(destDir, "_gitignore");
5229
5255
  if (!exists(packagedPath)) return;
5230
- import_fs2.default.renameSync(packagedPath, import_path2.default.join(destDir, ".gitignore"));
5256
+ import_fs3.default.renameSync(packagedPath, import_path3.default.join(destDir, ".gitignore"));
5257
+ }
5258
+ function materializeYarnrc(destDir) {
5259
+ const packagedPath = import_path3.default.join(destDir, "_yarnrc.yml");
5260
+ if (!exists(packagedPath)) return;
5261
+ import_fs3.default.renameSync(packagedPath, import_path3.default.join(destDir, ".yarnrc.yml"));
5231
5262
  }
5232
5263
  function replaceTokens(destDir, appName, description) {
5233
5264
  const pkgName = toPackageName(appName);
5234
5265
  const appTitle = toTitle(appName);
5235
- const pkgPath = import_path2.default.join(destDir, "package.json");
5266
+ const pkgPath = import_path3.default.join(destDir, "package.json");
5236
5267
  if (exists(pkgPath)) {
5237
5268
  const pkg = readJson(pkgPath);
5238
5269
  pkg["name"] = pkgName;
@@ -5247,7 +5278,7 @@ function replaceTokens(destDir, appName, description) {
5247
5278
  writeJson(pkgPath, pkg);
5248
5279
  logOk("package.json \u2014 name, description, version, stripped monorepo fields");
5249
5280
  }
5250
- const indexPath = import_path2.default.join(destDir, "index.html");
5281
+ const indexPath = import_path3.default.join(destDir, "index.html");
5251
5282
  if (exists(indexPath)) {
5252
5283
  let html = readText(indexPath);
5253
5284
  html = html.replace(
@@ -5265,7 +5296,7 @@ function replaceTokens(destDir, appName, description) {
5265
5296
  writeText(indexPath, html);
5266
5297
  logOk("index.html \u2014 title, meta description, application-name");
5267
5298
  }
5268
- const configPath = import_path2.default.join(destDir, "src", "configs", "appConfig.ts");
5299
+ const configPath = import_path3.default.join(destDir, "src", "configs", "appConfig.ts");
5269
5300
  if (exists(configPath)) {
5270
5301
  let cfg = readText(configPath);
5271
5302
  cfg = cfg.replace(
@@ -5275,7 +5306,7 @@ function replaceTokens(destDir, appName, description) {
5275
5306
  writeText(configPath, cfg);
5276
5307
  logOk("src/configs/appConfig.ts \u2014 default title");
5277
5308
  }
5278
- const readmePath = import_path2.default.join(destDir, "README.md");
5309
+ const readmePath = import_path3.default.join(destDir, "README.md");
5279
5310
  if (exists(readmePath)) {
5280
5311
  let readme = readText(readmePath);
5281
5312
  readme = readme.replace(
@@ -5289,7 +5320,7 @@ function replaceTokens(destDir, appName, description) {
5289
5320
  writeText(readmePath, readme);
5290
5321
  logOk("README.md \u2014 heading");
5291
5322
  }
5292
- const manifestPath = import_path2.default.join(destDir, "public", "manifest.webmanifest");
5323
+ const manifestPath = import_path3.default.join(destDir, "public", "manifest.webmanifest");
5293
5324
  if (exists(manifestPath)) {
5294
5325
  let manifest = readText(manifestPath);
5295
5326
  manifest = manifest.replace(
@@ -5301,7 +5332,7 @@ function replaceTokens(destDir, appName, description) {
5301
5332
  }
5302
5333
  }
5303
5334
  function patchDockerComposeForProject(destDir, appName, portOffset) {
5304
- const composePath = import_path2.default.join(destDir, "docker-compose.yml");
5335
+ const composePath = import_path3.default.join(destDir, "docker-compose.yml");
5305
5336
  if (!exists(composePath)) return;
5306
5337
  logStep("Patching docker-compose.yml \u2014 container names + ports");
5307
5338
  let content = readText(composePath);
@@ -5334,7 +5365,7 @@ function patchDockerComposeForProject(destDir, appName, portOffset) {
5334
5365
  }
5335
5366
  }
5336
5367
  function patchMakefileForProject(destDir, appName) {
5337
- const makefilePath = import_path2.default.join(destDir, "Makefile");
5368
+ const makefilePath = import_path3.default.join(destDir, "Makefile");
5338
5369
  if (!exists(makefilePath)) return;
5339
5370
  let content = readText(makefilePath);
5340
5371
  content = content.replace(
@@ -5347,7 +5378,7 @@ function patchMakefileForProject(destDir, appName) {
5347
5378
  logOk("Makefile \u2014 ensured dynamic container names");
5348
5379
  }
5349
5380
  function patchViteConfigPort(destDir, portOffset) {
5350
- const configPath = import_path2.default.join(destDir, "vite.config.ts");
5381
+ const configPath = import_path3.default.join(destDir, "vite.config.ts");
5351
5382
  if (!exists(configPath)) return;
5352
5383
  const devPort = 3e3 + portOffset;
5353
5384
  let content = readText(configPath);
@@ -5358,7 +5389,7 @@ function patchViteConfigPort(destDir, portOffset) {
5358
5389
  function patchEnvExamplePorts(destDir, portOffset) {
5359
5390
  const bePort = 8e3 + portOffset;
5360
5391
  for (const envFile of [".env.example", ".env.local"]) {
5361
- const envPath = import_path2.default.join(destDir, envFile);
5392
+ const envPath = import_path3.default.join(destDir, envFile);
5362
5393
  if (!exists(envPath)) continue;
5363
5394
  let content = readText(envPath);
5364
5395
  content = patchReplace(
@@ -5375,17 +5406,17 @@ function removeDocker(destDir) {
5375
5406
  const files = ["Dockerfile", "docker-compose.yml", ".dockerignore", "nginx.conf"];
5376
5407
  let deleted = 0;
5377
5408
  for (const f of files) {
5378
- if (safeUnlink(import_path2.default.join(destDir, f))) deleted++;
5409
+ if (safeUnlink(import_path3.default.join(destDir, f))) deleted++;
5379
5410
  }
5380
- if (safeRmDir(import_path2.default.join(destDir, "nginx"))) {
5411
+ if (safeRmDir(import_path3.default.join(destDir, "nginx"))) {
5381
5412
  deleted++;
5382
5413
  logInfo("nginx/ \u2014 removed");
5383
5414
  }
5384
- if (safeUnlink(import_path2.default.join(destDir, "Makefile"))) {
5415
+ if (safeUnlink(import_path3.default.join(destDir, "Makefile"))) {
5385
5416
  deleted++;
5386
5417
  logInfo("Makefile \u2014 removed");
5387
5418
  }
5388
- const readmePath = import_path2.default.join(destDir, "README.md");
5419
+ const readmePath = import_path3.default.join(destDir, "README.md");
5389
5420
  if (exists(readmePath)) {
5390
5421
  let readme = readText(readmePath);
5391
5422
  readme = removeMarkedSection(readme, "DOCKER");
@@ -5395,7 +5426,7 @@ function removeDocker(destDir) {
5395
5426
  writeText(readmePath, readme);
5396
5427
  logInfo("README.md \u2014 removed Docker sections");
5397
5428
  }
5398
- const envExPath = import_path2.default.join(destDir, ".env.example");
5429
+ const envExPath = import_path3.default.join(destDir, ".env.example");
5399
5430
  if (exists(envExPath)) {
5400
5431
  let env = readText(envExPath);
5401
5432
  env = env.replace(/# -{5,}\s*\n# 🐳 Docker[^#]*/g, "");
@@ -5409,16 +5440,16 @@ function removeDocker(destDir) {
5409
5440
  function removePwa(destDir) {
5410
5441
  let deleted = 0;
5411
5442
  for (const f of PWA_FILES) {
5412
- if (safeUnlink(import_path2.default.join(destDir, f))) deleted++;
5443
+ if (safeUnlink(import_path3.default.join(destDir, f))) deleted++;
5413
5444
  }
5414
- if (safeRmDir(import_path2.default.join(destDir, "public", "icons"))) deleted++;
5415
- safeRmDir(import_path2.default.join(destDir, "src", "sw"));
5416
- safeRmDir(import_path2.default.join(destDir, "src", "pwa"));
5445
+ if (safeRmDir(import_path3.default.join(destDir, "public", "icons"))) deleted++;
5446
+ safeRmDir(import_path3.default.join(destDir, "src", "sw"));
5447
+ safeRmDir(import_path3.default.join(destDir, "src", "pwa"));
5417
5448
  removePwaFromPackageJson(destDir);
5418
5449
  removePwaFromPluginsIndex(destDir);
5419
5450
  removePwaFromViteConfig(destDir);
5420
5451
  removePwaFromIndexHtml(destDir);
5421
- const readmePath = import_path2.default.join(destDir, "README.md");
5452
+ const readmePath = import_path3.default.join(destDir, "README.md");
5422
5453
  if (exists(readmePath)) {
5423
5454
  let readme = readText(readmePath);
5424
5455
  readme = removeMarkedSection(readme, "PWA");
@@ -5433,7 +5464,7 @@ function removePwa(destDir) {
5433
5464
  logOk(`Deleted ${deleted} PWA file(s)`);
5434
5465
  }
5435
5466
  function removePwaFromPackageJson(destDir) {
5436
- const pkgPath = import_path2.default.join(destDir, "package.json");
5467
+ const pkgPath = import_path3.default.join(destDir, "package.json");
5437
5468
  if (!exists(pkgPath)) return;
5438
5469
  const pkg = readJson(pkgPath);
5439
5470
  let changed = false;
@@ -5454,7 +5485,7 @@ function removePwaFromPackageJson(destDir) {
5454
5485
  removePkgScript(destDir, "setup:pwa");
5455
5486
  }
5456
5487
  function removePwaFromPluginsIndex(destDir) {
5457
- const indexPath = import_path2.default.join(destDir, "plugins", "index.ts");
5488
+ const indexPath = import_path3.default.join(destDir, "plugins", "index.ts");
5458
5489
  if (!exists(indexPath)) return;
5459
5490
  let content = readText(indexPath);
5460
5491
  content = content.replace(/^.*pwaPlugin.*\n/gm, "");
@@ -5466,7 +5497,7 @@ function removePwaFromPluginsIndex(destDir) {
5466
5497
  logInfo("plugins/index.ts \u2014 removed PWA plugin exports");
5467
5498
  }
5468
5499
  function removePwaFromViteConfig(destDir) {
5469
- const configPath = import_path2.default.join(destDir, "vite.config.ts");
5500
+ const configPath = import_path3.default.join(destDir, "vite.config.ts");
5470
5501
  if (!exists(configPath)) return;
5471
5502
  let content = readText(configPath);
5472
5503
  content = content.replace(
@@ -5487,7 +5518,7 @@ function removePwaFromViteConfig(destDir) {
5487
5518
  logInfo("vite.config.ts \u2014 removed PWA plugin usage");
5488
5519
  }
5489
5520
  function removePwaFromIndexHtml(destDir) {
5490
- const indexPath = import_path2.default.join(destDir, "index.html");
5521
+ const indexPath = import_path3.default.join(destDir, "index.html");
5491
5522
  if (!exists(indexPath)) return;
5492
5523
  let html = readText(indexPath);
5493
5524
  html = html.replace(/\s*<link rel="manifest"[^>]*>\s*/g, "\n ");
@@ -5505,7 +5536,7 @@ function removePwaFromIndexHtml(destDir) {
5505
5536
  logInfo("index.html \u2014 removed PWA meta tags and manifest link");
5506
5537
  }
5507
5538
  function removePwaFromMainTsx(destDir) {
5508
- const mainPath = import_path2.default.join(destDir, "src", "main.tsx");
5539
+ const mainPath = import_path3.default.join(destDir, "src", "main.tsx");
5509
5540
  if (!exists(mainPath)) return;
5510
5541
  let content = readText(mainPath);
5511
5542
  content = content.replace(/^import\s*\{[^}]*initPWA[^}]*\}\s*from\s*['"][^'"]*pwa['"];\s*\n/gm, "");
@@ -5515,7 +5546,7 @@ function removePwaFromMainTsx(destDir) {
5515
5546
  logInfo("src/main.tsx \u2014 removed initPWA import and call");
5516
5547
  }
5517
5548
  function removePwaFromAppTsx(destDir) {
5518
- const appPath = import_path2.default.join(destDir, "src", "App.tsx");
5549
+ const appPath = import_path3.default.join(destDir, "src", "App.tsx");
5519
5550
  if (!exists(appPath)) return;
5520
5551
  let content = readText(appPath);
5521
5552
  content = content.replace(/,\s*PwaUpdateBanner\b/g, "");
@@ -5526,11 +5557,11 @@ function removePwaFromAppTsx(destDir) {
5526
5557
  logInfo("src/App.tsx \u2014 removed PwaUpdateBanner");
5527
5558
  }
5528
5559
  function removeHusky(destDir) {
5529
- const huskyDir = import_path2.default.join(destDir, ".husky");
5560
+ const huskyDir = import_path3.default.join(destDir, ".husky");
5530
5561
  if (safeRmDir(huskyDir)) {
5531
5562
  logInfo(".husky/ \u2014 removed");
5532
5563
  }
5533
- const pkgPath = import_path2.default.join(destDir, "package.json");
5564
+ const pkgPath = import_path3.default.join(destDir, "package.json");
5534
5565
  if (exists(pkgPath)) {
5535
5566
  const pkg = readJson(pkgPath);
5536
5567
  let changed = false;
@@ -5570,7 +5601,7 @@ function addTailwind(destDir) {
5570
5601
  importTailwindInMain(destDir);
5571
5602
  }
5572
5603
  function addTailwindDeps(destDir) {
5573
- const pkgPath = import_path2.default.join(destDir, "package.json");
5604
+ const pkgPath = import_path3.default.join(destDir, "package.json");
5574
5605
  if (!exists(pkgPath)) return;
5575
5606
  const pkg = readJson(pkgPath);
5576
5607
  const devDeps = pkg["devDependencies"] ?? {};
@@ -5581,7 +5612,7 @@ function addTailwindDeps(destDir) {
5581
5612
  logOk("package.json \u2014 added tailwindcss + @tailwindcss/vite");
5582
5613
  }
5583
5614
  function addTailwindVitePlugin(destDir) {
5584
- const configPath = import_path2.default.join(destDir, "vite.config.ts");
5615
+ const configPath = import_path3.default.join(destDir, "vite.config.ts");
5585
5616
  if (!exists(configPath)) return;
5586
5617
  let content = readText(configPath);
5587
5618
  if (!content.includes("@tailwindcss/vite")) {
@@ -5652,12 +5683,12 @@ function createTailwindCss(destDir) {
5652
5683
  --radius-lg: var(--radius-4);
5653
5684
  }
5654
5685
  `;
5655
- const stylesDir = import_path2.default.join(destDir, "src", "styles");
5656
- writeText(import_path2.default.join(stylesDir, "tailwind.css"), tailwindCss);
5686
+ const stylesDir = import_path3.default.join(destDir, "src", "styles");
5687
+ writeText(import_path3.default.join(stylesDir, "tailwind.css"), tailwindCss);
5657
5688
  logOk("src/styles/tailwind.css \u2014 created with theme bridge");
5658
5689
  }
5659
5690
  function importTailwindInMain(destDir) {
5660
- const mainPath = import_path2.default.join(destDir, "src", "main.tsx");
5691
+ const mainPath = import_path3.default.join(destDir, "src", "main.tsx");
5661
5692
  if (!exists(mainPath)) return;
5662
5693
  let content = readText(mainPath);
5663
5694
  const tailwindImport = "import '@styles/tailwind.css';";
@@ -5691,7 +5722,7 @@ function cleanupSetupScripts(destDir, opts) {
5691
5722
  if (scriptsToRemove.length === 0) return;
5692
5723
  logStep("Removing unused setup scripts");
5693
5724
  for (const script of scriptsToRemove) {
5694
- const scriptPath = import_path2.default.join(destDir, "scripts", script);
5725
+ const scriptPath = import_path3.default.join(destDir, "scripts", script);
5695
5726
  if (safeUnlink(scriptPath)) {
5696
5727
  logOk(`scripts/${script} \u2014 removed`);
5697
5728
  }
@@ -5768,11 +5799,11 @@ function injectBrandColors(destDir, opts) {
5768
5799
  "}",
5769
5800
  ""
5770
5801
  ].join("\n");
5771
- const themesDir = import_path2.default.join(destDir, "src", "styles", "themes");
5772
- const brandPath = import_path2.default.join(themesDir, "_brand.scss");
5802
+ const themesDir = import_path3.default.join(destDir, "src", "styles", "themes");
5803
+ const brandPath = import_path3.default.join(themesDir, "_brand.scss");
5773
5804
  writeText(brandPath, brandScss);
5774
5805
  logOk("src/styles/themes/_brand.scss \u2014 created");
5775
- const indexPath = import_path2.default.join(themesDir, "_index.scss");
5806
+ const indexPath = import_path3.default.join(themesDir, "_index.scss");
5776
5807
  if (exists(indexPath)) {
5777
5808
  let idx = readText(indexPath);
5778
5809
  if (!idx.includes("@forward './brand'")) {
@@ -5783,8 +5814,8 @@ function injectBrandColors(destDir, opts) {
5783
5814
  }
5784
5815
  }
5785
5816
  function generateEnvLocal(destDir, appName) {
5786
- const examplePath = import_path2.default.join(destDir, ".env.example");
5787
- const localPath = import_path2.default.join(destDir, ".env.local");
5817
+ const examplePath = import_path3.default.join(destDir, ".env.example");
5818
+ const localPath = import_path3.default.join(destDir, ".env.local");
5788
5819
  if (!exists(examplePath)) {
5789
5820
  logInfo(".env.example not found \u2014 skipping .env.local generation");
5790
5821
  return;
@@ -5799,7 +5830,7 @@ function generateEnvLocal(destDir, appName) {
5799
5830
  logOk(".env.local generated from .env.example");
5800
5831
  }
5801
5832
  function cleanupGitignore(destDir) {
5802
- const gitignorePath = import_path2.default.join(destDir, ".gitignore");
5833
+ const gitignorePath = import_path3.default.join(destDir, ".gitignore");
5803
5834
  if (!exists(gitignorePath)) return;
5804
5835
  let content = readText(gitignorePath);
5805
5836
  const monorepoPatterns = [
@@ -5831,7 +5862,7 @@ function cleanupTemplateDocs(destDir) {
5831
5862
  logStep("Removing template documentation files");
5832
5863
  let removed = 0;
5833
5864
  for (const f of docFiles) {
5834
- if (safeUnlink(import_path2.default.join(destDir, f))) {
5865
+ if (safeUnlink(import_path3.default.join(destDir, f))) {
5835
5866
  logOk(`${f} \u2014 removed`);
5836
5867
  removed++;
5837
5868
  }
@@ -5867,7 +5898,7 @@ function initGitRepo(destDir, appName) {
5867
5898
  }
5868
5899
  }
5869
5900
  function removePkgScript(destDir, scriptName) {
5870
- const pkgPath = import_path2.default.join(destDir, "package.json");
5901
+ const pkgPath = import_path3.default.join(destDir, "package.json");
5871
5902
  if (!exists(pkgPath)) return;
5872
5903
  const pkg = readJson(pkgPath);
5873
5904
  const scripts = pkg["scripts"];
@@ -5876,14 +5907,15 @@ function removePkgScript(destDir, scriptName) {
5876
5907
  writeJson(pkgPath, pkg);
5877
5908
  }
5878
5909
  }
5879
- var import_fs2, import_path2, import_node_url, TEMPLATE_TITLE, TEMPLATE_DESCRIPTION, PWA_FILES;
5910
+ var import_fs3, import_path3, import_node_url2, TEMPLATE_TITLE, TEMPLATE_DESCRIPTION, PWA_FILES;
5880
5911
  var init_scaffold = __esm({
5881
5912
  "src/commands/web/scaffold.ts"() {
5882
5913
  "use strict";
5883
5914
  init_cjs_shims();
5884
- import_fs2 = __toESM(require("fs"), 1);
5885
- import_path2 = __toESM(require("path"), 1);
5886
- import_node_url = require("url");
5915
+ import_fs3 = __toESM(require("fs"), 1);
5916
+ import_path3 = __toESM(require("path"), 1);
5917
+ import_node_url2 = require("url");
5918
+ init_package_paths();
5887
5919
  init_spinner();
5888
5920
  init_utils();
5889
5921
  TEMPLATE_TITLE = "React Starter Kit";
@@ -5974,7 +6006,7 @@ async function runWebCommand(argv) {
5974
6006
  return;
5975
6007
  }
5976
6008
  if (args.includes("--version") || args.includes("-v")) {
5977
- console.log("1.0.3");
6009
+ console.log("1.0.5");
5978
6010
  return;
5979
6011
  }
5980
6012
  printBanner();
@@ -5989,7 +6021,7 @@ async function runWebCommand(argv) {
5989
6021
  const destDir2 = getDestDir(appName2);
5990
6022
  const pm2 = detectDefaultPm();
5991
6023
  console.log("");
5992
- logStep(`Creating "${appName2}" (non-interactive) in ${import_path3.default.relative(process.cwd(), import_path3.default.dirname(destDir2))}`);
6024
+ logStep(`Creating "${appName2}" (non-interactive) in ${import_path4.default.relative(process.cwd(), import_path4.default.dirname(destDir2))}`);
5993
6025
  try {
5994
6026
  await scaffold({
5995
6027
  appName: appName2,
@@ -6138,7 +6170,7 @@ async function runWebCommand(argv) {
6138
6170
  const appName = toPackageName(argName ?? answers.appName ?? "my-web-app");
6139
6171
  const destDir = getDestDir(appName);
6140
6172
  console.log("");
6141
- logStep(`Creating "${appName}" in ${import_path3.default.relative(process.cwd(), import_path3.default.dirname(destDir))}`);
6173
+ logStep(`Creating "${appName}" in ${import_path4.default.relative(process.cwd(), import_path4.default.dirname(destDir))}`);
6142
6174
  try {
6143
6175
  await scaffold({
6144
6176
  appName,
@@ -6178,19 +6210,19 @@ async function runWebCommand(argv) {
6178
6210
  function printBanner() {
6179
6211
  console.log("");
6180
6212
  console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
6181
- console.log(` \u2551 create-basekit web v${"1.0.3"} \u2551`);
6213
+ console.log(` \u2551 create-basekit web v${"1.0.5"} \u2551`);
6182
6214
  console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
6183
6215
  console.log("");
6184
6216
  }
6185
6217
  function abortIfInsideWorkspace(argv) {
6186
6218
  const exampleName = argv.find((a) => !a.startsWith("-") && !isValueOfFlag(argv, a))?.trim() ?? "my-app";
6187
6219
  let dir = process.cwd();
6188
- const root = import_path3.default.parse(dir).root;
6220
+ const root = import_path4.default.parse(dir).root;
6189
6221
  while (dir !== root) {
6190
- const pkgPath = import_path3.default.join(dir, "package.json");
6191
- if (import_fs3.default.existsSync(pkgPath)) {
6222
+ const pkgPath = import_path4.default.join(dir, "package.json");
6223
+ if (import_fs4.default.existsSync(pkgPath)) {
6192
6224
  try {
6193
- const pkg = JSON.parse(import_fs3.default.readFileSync(pkgPath, "utf8"));
6225
+ const pkg = JSON.parse(import_fs4.default.readFileSync(pkgPath, "utf8"));
6194
6226
  if (pkg.workspaces) {
6195
6227
  console.error("");
6196
6228
  console.error(" \u2717 Cannot create app inside a Yarn workspace.");
@@ -6212,7 +6244,7 @@ function abortIfInsideWorkspace(argv) {
6212
6244
  } catch {
6213
6245
  }
6214
6246
  }
6215
- const parent = import_path3.default.dirname(dir);
6247
+ const parent = import_path4.default.dirname(dir);
6216
6248
  if (parent === dir) break;
6217
6249
  dir = parent;
6218
6250
  }
@@ -6283,13 +6315,13 @@ function isValueOfFlag(args, value) {
6283
6315
  const prev = args[idx - 1];
6284
6316
  return prev !== void 0 && prev.startsWith("--");
6285
6317
  }
6286
- var import_path3, import_fs3, import_prompts;
6318
+ var import_path4, import_fs4, import_prompts;
6287
6319
  var init_web = __esm({
6288
6320
  "src/commands/web/index.ts"() {
6289
6321
  "use strict";
6290
6322
  init_cjs_shims();
6291
- import_path3 = __toESM(require("path"), 1);
6292
- import_fs3 = __toESM(require("fs"), 1);
6323
+ import_path4 = __toESM(require("path"), 1);
6324
+ import_fs4 = __toESM(require("fs"), 1);
6293
6325
  import_prompts = __toESM(require_prompts3(), 1);
6294
6326
  init_scaffold();
6295
6327
  init_install();
@@ -6312,21 +6344,21 @@ function logError2(msg) {
6312
6344
  console.error(` \u2717 ${msg}`);
6313
6345
  }
6314
6346
  function exists2(absPath) {
6315
- return import_fs4.default.existsSync(absPath);
6347
+ return import_fs5.default.existsSync(absPath);
6316
6348
  }
6317
6349
  function copyDir2(src, dest) {
6318
- import_fs4.default.cpSync(src, dest, { recursive: true });
6350
+ import_fs5.default.cpSync(src, dest, { recursive: true });
6319
6351
  }
6320
6352
  function readText2(absPath) {
6321
- return import_fs4.default.readFileSync(absPath, "utf-8");
6353
+ return import_fs5.default.readFileSync(absPath, "utf-8");
6322
6354
  }
6323
6355
  function writeText2(absPath, content) {
6324
- import_fs4.default.writeFileSync(absPath, content);
6356
+ import_fs5.default.writeFileSync(absPath, content);
6325
6357
  }
6326
6358
  function safeRmDir2(absDir) {
6327
6359
  if (!exists2(absDir)) return false;
6328
6360
  try {
6329
- import_fs4.default.rmSync(absDir, { recursive: true, force: true });
6361
+ import_fs5.default.rmSync(absDir, { recursive: true, force: true });
6330
6362
  return true;
6331
6363
  } catch {
6332
6364
  return false;
@@ -6387,16 +6419,16 @@ function toSlug(name) {
6387
6419
  return toKebab(name).replace(/-/g, "");
6388
6420
  }
6389
6421
  function getDestDir2(appName) {
6390
- return import_path4.default.resolve(process.cwd(), appName);
6422
+ return import_path5.default.resolve(process.cwd(), appName);
6391
6423
  }
6392
- var import_child_process2, import_fs4, import_path4;
6424
+ var import_child_process2, import_fs5, import_path5;
6393
6425
  var init_utils2 = __esm({
6394
6426
  "src/commands/mobile/utils.ts"() {
6395
6427
  "use strict";
6396
6428
  init_cjs_shims();
6397
6429
  import_child_process2 = require("child_process");
6398
- import_fs4 = __toESM(require("fs"), 1);
6399
- import_path4 = __toESM(require("path"), 1);
6430
+ import_fs5 = __toESM(require("fs"), 1);
6431
+ import_path5 = __toESM(require("path"), 1);
6400
6432
  }
6401
6433
  });
6402
6434
 
@@ -6437,17 +6469,9 @@ var init_spinner2 = __esm({
6437
6469
 
6438
6470
  // src/commands/mobile/scaffold.ts
6439
6471
  function templateDir2() {
6440
- const here = import_path5.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
6441
- const candidates = [
6442
- import_path5.default.join(here, "../../../template-mobile"),
6443
- import_path5.default.join(here, "../template-mobile")
6444
- ];
6445
- for (const candidate of candidates) {
6446
- const resolved = import_path5.default.resolve(candidate);
6447
- if (import_fs5.default.existsSync(resolved)) return resolved;
6448
- }
6449
- throw new Error(
6450
- `Starter template is missing (searched: ${candidates.map((c) => import_path5.default.resolve(c)).join(", ")})`
6472
+ return import_path6.default.join(
6473
+ getCreateBasekitPackageRoot(import_path6.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl))),
6474
+ "template-mobile"
6451
6475
  );
6452
6476
  }
6453
6477
  async function scaffold2(opts) {
@@ -6471,6 +6495,7 @@ async function scaffoldInner2(opts) {
6471
6495
  const apiUrl = `http://localhost:${8e3 + portOffset}/api/v1`;
6472
6496
  await cloneTemplate2(destDir);
6473
6497
  materializeGitignore2(destDir);
6498
+ materializeYarnrc2(destDir);
6474
6499
  if (opts.monorepo) {
6475
6500
  stripBundledShared(destDir);
6476
6501
  adjustSharedPaths(destDir);
@@ -6492,7 +6517,7 @@ async function scaffoldInner2(opts) {
6492
6517
  }
6493
6518
  async function cloneTemplate2(destDir) {
6494
6519
  const source = templateDir2();
6495
- if (!import_fs5.default.existsSync(source)) {
6520
+ if (!import_fs6.default.existsSync(source)) {
6496
6521
  throw new Error(`Starter template is missing at ${source}`);
6497
6522
  }
6498
6523
  logStep2("Copying the Expo starter");
@@ -6507,12 +6532,17 @@ async function cloneTemplate2(destDir) {
6507
6532
  spinner.succeed("Template copied");
6508
6533
  }
6509
6534
  function materializeGitignore2(destDir) {
6510
- const packagedPath = import_path5.default.join(destDir, "_gitignore");
6535
+ const packagedPath = import_path6.default.join(destDir, "_gitignore");
6511
6536
  if (!exists2(packagedPath)) return;
6512
- import_fs5.default.renameSync(packagedPath, import_path5.default.join(destDir, ".gitignore"));
6537
+ import_fs6.default.renameSync(packagedPath, import_path6.default.join(destDir, ".gitignore"));
6538
+ }
6539
+ function materializeYarnrc2(destDir) {
6540
+ const packagedPath = import_path6.default.join(destDir, "_yarnrc.yml");
6541
+ if (!exists2(packagedPath)) return;
6542
+ import_fs6.default.renameSync(packagedPath, import_path6.default.join(destDir, ".yarnrc.yml"));
6513
6543
  }
6514
6544
  function stripBundledShared(destDir) {
6515
- const sharedDir = import_path5.default.join(destDir, "packages");
6545
+ const sharedDir = import_path6.default.join(destDir, "packages");
6516
6546
  if (exists2(sharedDir)) {
6517
6547
  safeRmDir2(sharedDir);
6518
6548
  logOk3("Stripped bundled packages/shared/ (orchestrator provides this)");
@@ -6520,30 +6550,30 @@ function stripBundledShared(destDir) {
6520
6550
  }
6521
6551
  function adjustSharedPaths(destDir) {
6522
6552
  patchFile(
6523
- import_path5.default.join(destDir, "tsconfig.json"),
6553
+ import_path6.default.join(destDir, "tsconfig.json"),
6524
6554
  "./packages/shared/src/*",
6525
6555
  "../packages/shared/src/*",
6526
6556
  "tsconfig @shared/* path"
6527
6557
  );
6528
6558
  patchFile(
6529
- import_path5.default.join(destDir, "tsconfig.json"),
6559
+ import_path6.default.join(destDir, "tsconfig.json"),
6530
6560
  '"packages/shared/src"',
6531
6561
  '"../packages/shared/src"',
6532
6562
  "tsconfig include path"
6533
6563
  );
6534
6564
  patchFile(
6535
- import_path5.default.join(destDir, "tsconfig.json"),
6565
+ import_path6.default.join(destDir, "tsconfig.json"),
6536
6566
  '"packages/shared/src/**/__tests__/**"',
6537
6567
  '"../packages/shared/src/**/__tests__/**"',
6538
6568
  "tsconfig exclude path"
6539
6569
  );
6540
6570
  patchFile(
6541
- import_path5.default.join(destDir, "babel.config.js"),
6571
+ import_path6.default.join(destDir, "babel.config.js"),
6542
6572
  "'./packages/shared/src'",
6543
6573
  "'../packages/shared/src'",
6544
6574
  "babel @shared alias"
6545
6575
  );
6546
- const metroPath = import_path5.default.join(destDir, "metro.config.js");
6576
+ const metroPath = import_path6.default.join(destDir, "metro.config.js");
6547
6577
  if (exists2(metroPath)) {
6548
6578
  let metro = readText2(metroPath);
6549
6579
  metro = metro.replace(
@@ -6560,7 +6590,7 @@ function adjustSharedPaths(destDir) {
6560
6590
  writeText2(metroPath, metro);
6561
6591
  logOk3("Adjusted metro.config.js for monorepo layout");
6562
6592
  }
6563
- const pkgPath = import_path5.default.join(destDir, "package.json");
6593
+ const pkgPath = import_path6.default.join(destDir, "package.json");
6564
6594
  if (exists2(pkgPath)) {
6565
6595
  let pkgText = readText2(pkgPath);
6566
6596
  pkgText = pkgText.replace(
@@ -6582,7 +6612,7 @@ function replaceTokens2(destDir, tokens) {
6582
6612
  "README.md"
6583
6613
  ];
6584
6614
  for (const relPath of targetFiles) {
6585
- const absPath = import_path5.default.join(destDir, relPath);
6615
+ const absPath = import_path6.default.join(destDir, relPath);
6586
6616
  if (!exists2(absPath)) continue;
6587
6617
  let content = readText2(absPath);
6588
6618
  let changed = false;
@@ -6599,8 +6629,8 @@ function replaceTokens2(destDir, tokens) {
6599
6629
  }
6600
6630
  }
6601
6631
  function createDotEnv(destDir, apiUrl) {
6602
- const examplePath = import_path5.default.join(destDir, ".env.example");
6603
- const envPath = import_path5.default.join(destDir, ".env");
6632
+ const examplePath = import_path6.default.join(destDir, ".env.example");
6633
+ const envPath = import_path6.default.join(destDir, ".env");
6604
6634
  if (exists2(examplePath)) {
6605
6635
  let content = readText2(examplePath);
6606
6636
  content = content.replace(
@@ -6612,18 +6642,18 @@ function createDotEnv(destDir, apiUrl) {
6612
6642
  }
6613
6643
  }
6614
6644
  function cleanupArtifacts(destDir) {
6615
- safeRmDir2(import_path5.default.join(destDir, ".git"));
6616
- const lockPath = import_path5.default.join(destDir, "yarn.lock");
6645
+ safeRmDir2(import_path6.default.join(destDir, ".git"));
6646
+ const lockPath = import_path6.default.join(destDir, "yarn.lock");
6617
6647
  if (exists2(lockPath)) {
6618
- import_fs5.default.unlinkSync(lockPath);
6648
+ import_fs6.default.unlinkSync(lockPath);
6619
6649
  }
6620
- const readmePath = import_path5.default.join(destDir, "README.md");
6650
+ const readmePath = import_path6.default.join(destDir, "README.md");
6621
6651
  if (exists2(readmePath)) {
6622
- import_fs5.default.unlinkSync(readmePath);
6652
+ import_fs6.default.unlinkSync(readmePath);
6623
6653
  }
6624
- const licensePath = import_path5.default.join(destDir, "LICENSE");
6654
+ const licensePath = import_path6.default.join(destDir, "LICENSE");
6625
6655
  if (exists2(licensePath)) {
6626
- import_fs5.default.unlinkSync(licensePath);
6656
+ import_fs6.default.unlinkSync(licensePath);
6627
6657
  }
6628
6658
  }
6629
6659
  function initGitRepo2(destDir) {
@@ -6642,14 +6672,15 @@ function patchFile(absPath, search, replacement, description) {
6642
6672
  }
6643
6673
  writeText2(absPath, content.replace(search, replacement));
6644
6674
  }
6645
- var import_fs5, import_path5, import_node_url2;
6675
+ var import_fs6, import_path6, import_node_url3;
6646
6676
  var init_scaffold2 = __esm({
6647
6677
  "src/commands/mobile/scaffold.ts"() {
6648
6678
  "use strict";
6649
6679
  init_cjs_shims();
6650
- import_fs5 = __toESM(require("fs"), 1);
6651
- import_path5 = __toESM(require("path"), 1);
6652
- import_node_url2 = require("url");
6680
+ import_fs6 = __toESM(require("fs"), 1);
6681
+ import_path6 = __toESM(require("path"), 1);
6682
+ import_node_url3 = require("url");
6683
+ init_package_paths();
6653
6684
  init_utils2();
6654
6685
  init_spinner2();
6655
6686
  }
@@ -6716,7 +6747,7 @@ async function runMobileCommand(argv) {
6716
6747
  return;
6717
6748
  }
6718
6749
  if (args.includes("--version") || args.includes("-v")) {
6719
- console.log("1.0.3");
6750
+ console.log("1.0.5");
6720
6751
  return;
6721
6752
  }
6722
6753
  printBanner2();
@@ -6819,7 +6850,7 @@ async function runMobileCommand(argv) {
6819
6850
  function printBanner2() {
6820
6851
  console.log("");
6821
6852
  console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
6822
- console.log(` \u2551 create-basekit mobile v${"1.0.3"} \u2551`);
6853
+ console.log(` \u2551 create-basekit mobile v${"1.0.5"} \u2551`);
6823
6854
  console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
6824
6855
  console.log("");
6825
6856
  console.log(" Expo (React Native) app scaffolder");
@@ -6918,16 +6949,16 @@ function logError3(msg) {
6918
6949
  console.error(` \u2717 ${msg}`);
6919
6950
  }
6920
6951
  function exists3(absPath) {
6921
- return import_fs6.default.existsSync(absPath);
6952
+ return import_fs7.default.existsSync(absPath);
6922
6953
  }
6923
6954
  function mkdirp(absPath) {
6924
- import_fs6.default.mkdirSync(absPath, { recursive: true });
6955
+ import_fs7.default.mkdirSync(absPath, { recursive: true });
6925
6956
  }
6926
6957
  function readText3(absPath) {
6927
- return import_fs6.default.readFileSync(absPath, "utf-8");
6958
+ return import_fs7.default.readFileSync(absPath, "utf-8");
6928
6959
  }
6929
6960
  function writeText3(absPath, content) {
6930
- import_fs6.default.writeFileSync(absPath, content);
6961
+ import_fs7.default.writeFileSync(absPath, content);
6931
6962
  }
6932
6963
  function exec3(cmd, cwd, stdio = "pipe") {
6933
6964
  try {
@@ -7026,13 +7057,13 @@ function toKebab2(name) {
7026
7057
  function toPythonSlug(name) {
7027
7058
  return toKebab2(name).replace(/-/g, "_");
7028
7059
  }
7029
- var import_child_process3, import_fs6;
7060
+ var import_child_process3, import_fs7;
7030
7061
  var init_utils3 = __esm({
7031
7062
  "src/commands/app/utils.ts"() {
7032
7063
  "use strict";
7033
7064
  init_cjs_shims();
7034
7065
  import_child_process3 = require("child_process");
7035
- import_fs6 = __toESM(require("fs"), 1);
7066
+ import_fs7 = __toESM(require("fs"), 1);
7036
7067
  }
7037
7068
  });
7038
7069
 
@@ -7163,18 +7194,6 @@ var init_spinner3 = __esm({
7163
7194
  });
7164
7195
 
7165
7196
  // src/commands/app/resolve-django-base.ts
7166
- function getWebKitRootFromPackage() {
7167
- let dir = import_path6.default.resolve(__dirname);
7168
- for (let i = 0; i < 6; i += 1) {
7169
- if (import_fs7.default.existsSync(import_path6.default.join(dir, "packages", "utils", "package.json"))) {
7170
- return dir;
7171
- }
7172
- const parent = import_path6.default.dirname(dir);
7173
- if (parent === dir) break;
7174
- dir = parent;
7175
- }
7176
- return import_path6.default.resolve(__dirname, "../../..");
7177
- }
7178
7197
  function djangoBasePipInstallArg(spec) {
7179
7198
  if (spec.kind === "pypi") {
7180
7199
  return spec.pipSpec;
@@ -7182,19 +7201,19 @@ function djangoBasePipInstallArg(spec) {
7182
7201
  return `appsbyme-basekit[cli] @ ${spec.pipSpec}`;
7183
7202
  }
7184
7203
  function isUsableCheckout(dir) {
7185
- return import_fs7.default.existsSync(dir) && (import_fs7.default.existsSync(import_path6.default.join(dir, "pyproject.toml")) || import_fs7.default.existsSync(import_path6.default.join(dir, "setup.py")) || import_fs7.default.existsSync(import_path6.default.join(dir, "setup.cfg")));
7204
+ return import_fs8.default.existsSync(dir) && (import_fs8.default.existsSync(import_path7.default.join(dir, "pyproject.toml")) || import_fs8.default.existsSync(import_path7.default.join(dir, "setup.py")) || import_fs8.default.existsSync(import_path7.default.join(dir, "setup.cfg")));
7186
7205
  }
7187
7206
  function filePipSpec(absoluteDir) {
7188
- const resolved = import_path6.default.resolve(absoluteDir);
7207
+ const resolved = import_path7.default.resolve(absoluteDir);
7189
7208
  const normalized = resolved.replace(/\\/g, "/");
7190
7209
  const withSlash = normalized.startsWith("/") ? normalized : `/${normalized}`;
7191
7210
  return `file://${withSlash}`;
7192
7211
  }
7193
- function resolveDjangoBaseInstallSpec(opts) {
7212
+ function resolveDjangoBaseInstallSpec(opts = {}) {
7194
7213
  const env = opts.env ?? process.env;
7195
7214
  const envPath = env.BASEKIT_DJANGO_BASE?.trim();
7196
7215
  if (envPath) {
7197
- const absolute = import_path6.default.resolve(envPath);
7216
+ const absolute = import_path7.default.resolve(envPath);
7198
7217
  if (isUsableCheckout(absolute)) {
7199
7218
  return {
7200
7219
  kind: "file",
@@ -7203,46 +7222,28 @@ function resolveDjangoBaseInstallSpec(opts) {
7203
7222
  };
7204
7223
  }
7205
7224
  }
7206
- const besideProject = import_path6.default.resolve(opts.projectRoot, "..", "basekit");
7207
- if (isUsableCheckout(besideProject)) {
7208
- return {
7209
- kind: "file",
7210
- pipSpec: filePipSpec(besideProject),
7211
- label: `sibling beside project (${besideProject})`
7212
- };
7213
- }
7214
- if (opts.webKitRoot) {
7215
- const besideWebKit = import_path6.default.resolve(opts.webKitRoot, "..", "basekit");
7216
- if (isUsableCheckout(besideWebKit)) {
7217
- return {
7218
- kind: "file",
7219
- pipSpec: filePipSpec(besideWebKit),
7220
- label: `sibling beside basekit-web (${besideWebKit})`
7221
- };
7222
- }
7223
- }
7224
7225
  return {
7225
7226
  kind: "pypi",
7226
7227
  pipSpec: DJANGO_BASE_PYPI_REQUIREMENT,
7227
7228
  label: "PyPI (appsbyme-basekit)"
7228
7229
  };
7229
7230
  }
7230
- var import_fs7, import_path6, DJANGO_BASE_PYPI_REQUIREMENT;
7231
+ var import_fs8, import_path7, DJANGO_BASE_PYPI_REQUIREMENT;
7231
7232
  var init_resolve_django_base = __esm({
7232
7233
  "src/commands/app/resolve-django-base.ts"() {
7233
7234
  "use strict";
7234
7235
  init_cjs_shims();
7235
- import_fs7 = __toESM(require("fs"), 1);
7236
- import_path6 = __toESM(require("path"), 1);
7237
- DJANGO_BASE_PYPI_REQUIREMENT = "appsbyme-basekit[cli]==1.0.0";
7236
+ import_fs8 = __toESM(require("fs"), 1);
7237
+ import_path7 = __toESM(require("path"), 1);
7238
+ DJANGO_BASE_PYPI_REQUIREMENT = "appsbyme-basekit[cli]>=1.0.0";
7238
7239
  }
7239
7240
  });
7240
7241
 
7241
7242
  // src/commands/app/backend.ts
7242
7243
  async function scaffoldBackend(opts) {
7243
7244
  const { projectName, portOffset, projectRoot, nonInteractive, includeMobile } = opts;
7244
- const backendDir = import_path7.default.join(projectRoot, "backend");
7245
- const venvDir = import_path7.default.join(backendDir, ".venv");
7245
+ const backendDir = import_path8.default.join(projectRoot, "backend");
7246
+ const venvDir = import_path8.default.join(backendDir, ".venv");
7246
7247
  const pythonCmd = getPythonCommand();
7247
7248
  logStep3("Setting up Python virtual environment");
7248
7249
  mkdirp(backendDir);
@@ -7255,10 +7256,7 @@ async function scaffoldBackend(opts) {
7255
7256
  }
7256
7257
  venvSpinner.succeed("Virtual environment created at backend/.venv");
7257
7258
  const pip = venvBin(venvDir, "pip");
7258
- const djangoBase = resolveDjangoBaseInstallSpec({
7259
- projectRoot,
7260
- webKitRoot: getWebKitRootFromPackage()
7261
- });
7259
+ const djangoBase = resolveDjangoBaseInstallSpec();
7262
7260
  const pipArg = djangoBasePipInstallArg(djangoBase);
7263
7261
  const installSpinner = createSpinner3(`Installing appsbyme-basekit[cli] from ${djangoBase.label}\u2026`);
7264
7262
  const installResult = await execAsync3(
@@ -7292,7 +7290,7 @@ async function scaffoldBackend(opts) {
7292
7290
  } finally {
7293
7291
  safeUnlink2(configPath);
7294
7292
  }
7295
- const nestedDir = import_path7.default.join(backendDir, slug);
7293
+ const nestedDir = import_path8.default.join(backendDir, slug);
7296
7294
  if (exists3(nestedDir)) {
7297
7295
  hoistDirectory(nestedDir, backendDir);
7298
7296
  }
@@ -7300,7 +7298,7 @@ async function scaffoldBackend(opts) {
7300
7298
  }
7301
7299
  function venvBin(venvDir, name) {
7302
7300
  const binDir = process.platform === "win32" ? "Scripts" : "bin";
7303
- return import_path7.default.join(venvDir, binDir, name);
7301
+ return import_path8.default.join(venvDir, binDir, name);
7304
7302
  }
7305
7303
  function writeTempConfig(projectName, portOffset, includeMobile) {
7306
7304
  const slug = toPythonSlug(projectName);
@@ -7339,38 +7337,38 @@ function writeTempConfig(projectName, portOffset, includeMobile) {
7339
7337
  }
7340
7338
  const yaml = lines.join("\n") + "\n";
7341
7339
  const tmpDir = import_os.default.tmpdir();
7342
- const configPath = import_path7.default.join(tmpDir, `basekit-config-${Date.now()}.yml`);
7340
+ const configPath = import_path8.default.join(tmpDir, `basekit-config-${Date.now()}.yml`);
7343
7341
  writeText3(configPath, yaml);
7344
7342
  return configPath;
7345
7343
  }
7346
7344
  function hoistDirectory(srcDir, destDir) {
7347
7345
  const tempDir = srcDir + `_basekit_hoist_${Date.now()}`;
7348
- import_fs8.default.renameSync(srcDir, tempDir);
7349
- for (const entry of import_fs8.default.readdirSync(tempDir)) {
7350
- const src = import_path7.default.join(tempDir, entry);
7351
- const dest = import_path7.default.join(destDir, entry);
7346
+ import_fs9.default.renameSync(srcDir, tempDir);
7347
+ for (const entry of import_fs9.default.readdirSync(tempDir)) {
7348
+ const src = import_path8.default.join(tempDir, entry);
7349
+ const dest = import_path8.default.join(destDir, entry);
7352
7350
  if (exists3(dest)) {
7353
7351
  logInfo2(`hoist: skipped "${entry}" \u2014 already exists at destination`);
7354
7352
  continue;
7355
7353
  }
7356
- import_fs8.default.renameSync(src, dest);
7354
+ import_fs9.default.renameSync(src, dest);
7357
7355
  }
7358
- import_fs8.default.rmSync(tempDir, { recursive: true, force: true });
7356
+ import_fs9.default.rmSync(tempDir, { recursive: true, force: true });
7359
7357
  }
7360
7358
  function safeUnlink2(absPath) {
7361
7359
  try {
7362
- if (exists3(absPath)) import_fs8.default.unlinkSync(absPath);
7360
+ if (exists3(absPath)) import_fs9.default.unlinkSync(absPath);
7363
7361
  } catch {
7364
7362
  }
7365
7363
  }
7366
- var import_path7, import_os, import_fs8;
7364
+ var import_path8, import_os, import_fs9;
7367
7365
  var init_backend = __esm({
7368
7366
  "src/commands/app/backend.ts"() {
7369
7367
  "use strict";
7370
7368
  init_cjs_shims();
7371
- import_path7 = __toESM(require("path"), 1);
7369
+ import_path8 = __toESM(require("path"), 1);
7372
7370
  import_os = __toESM(require("os"), 1);
7373
- import_fs8 = __toESM(require("fs"), 1);
7371
+ import_fs9 = __toESM(require("fs"), 1);
7374
7372
  init_spinner3();
7375
7373
  init_utils3();
7376
7374
  init_prereqs();
@@ -7378,87 +7376,9 @@ var init_backend = __esm({
7378
7376
  }
7379
7377
  });
7380
7378
 
7381
- // src/commands/app/local-basekit-pins.ts
7382
- function webKitPackagesAvailable(webKitRoot) {
7383
- return exists3(import_path8.default.join(webKitRoot, "packages", "utils", "package.json"));
7384
- }
7385
- function toFileSpecifier(fromDir, absolutePackageDir) {
7386
- let relative = import_path8.default.relative(fromDir, absolutePackageDir);
7387
- if (!relative.startsWith(".")) {
7388
- relative = `./${relative}`;
7389
- }
7390
- return `file:${relative.replace(/\\/g, "/")}`;
7391
- }
7392
- function rewriteSection(section, frontendDir, webKitRoot) {
7393
- if (!section) return 0;
7394
- let count = 0;
7395
- for (const [name, dirName] of Object.entries(BASEKIT_PACKAGE_DIRS)) {
7396
- if (!(name in section)) continue;
7397
- const absolute = import_path8.default.join(webKitRoot, "packages", dirName);
7398
- if (!exists3(import_path8.default.join(absolute, "package.json"))) {
7399
- logWarn3(`Local package missing for ${name} \u2014 left as ${section[name]}`);
7400
- continue;
7401
- }
7402
- section[name] = toFileSpecifier(frontendDir, absolute);
7403
- count += 1;
7404
- }
7405
- return count;
7406
- }
7407
- function applyLocalBasekitPins(projectRoot, webKitRoot = getWebKitRootFromPackage()) {
7408
- if (!webKitPackagesAvailable(webKitRoot)) {
7409
- logInfo2("Local basekit-web packages not found \u2014 leaving @appsbyme registry pins");
7410
- return false;
7411
- }
7412
- const packageJsonPath = import_path8.default.join(projectRoot, "frontend", "package.json");
7413
- if (!exists3(packageJsonPath)) {
7414
- logWarn3("frontend/package.json not found \u2014 skipping local @appsbyme pins");
7415
- return false;
7416
- }
7417
- const pkg = JSON.parse(readText3(packageJsonPath));
7418
- const frontendDir = import_path8.default.join(projectRoot, "frontend");
7419
- let rewritten = rewriteSection(pkg.dependencies, frontendDir, webKitRoot) + rewriteSection(pkg.devDependencies, frontendDir, webKitRoot) + rewriteSection(pkg.resolutions, frontendDir, webKitRoot);
7420
- if (!pkg.resolutions) pkg.resolutions = {};
7421
- for (const [name, dirName] of Object.entries(BASEKIT_PACKAGE_DIRS)) {
7422
- const absolute = import_path8.default.join(webKitRoot, "packages", dirName);
7423
- if (!exists3(import_path8.default.join(absolute, "package.json"))) continue;
7424
- const next = toFileSpecifier(frontendDir, absolute);
7425
- if (pkg.resolutions[name] !== next) {
7426
- pkg.resolutions[name] = next;
7427
- rewritten += 1;
7428
- }
7429
- }
7430
- if (rewritten === 0) {
7431
- logInfo2("No @appsbyme pins to rewrite in frontend/package.json");
7432
- return false;
7433
- }
7434
- writeText3(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
7435
- `);
7436
- logOk4(`Rewrote ${rewritten} @appsbyme pin(s) to local file: paths`);
7437
- return true;
7438
- }
7439
- var import_path8, BASEKIT_PACKAGE_DIRS;
7440
- var init_local_basekit_pins = __esm({
7441
- "src/commands/app/local-basekit-pins.ts"() {
7442
- "use strict";
7443
- init_cjs_shims();
7444
- import_path8 = __toESM(require("path"), 1);
7445
- init_resolve_django_base();
7446
- init_utils3();
7447
- BASEKIT_PACKAGE_DIRS = {
7448
- "@appsbyme/basekit-hooks": "hooks",
7449
- "@appsbyme/basekit-styles": "styles",
7450
- "@appsbyme/basekit-ui": "ui",
7451
- "@appsbyme/basekit-utils": "utils",
7452
- "@appsbyme/basekit-eslint-config": "eslint-config",
7453
- "@appsbyme/basekit-stylelint-config": "stylelint-config",
7454
- "@appsbyme/basekit-tsconfig": "tsconfig"
7455
- };
7456
- }
7457
- });
7458
-
7459
7379
  // src/commands/app/yarn-install.ts
7460
7380
  async function yarnInstall(dir, label) {
7461
- if (!import_fs9.default.existsSync(import_path9.default.join(dir, "package.json"))) {
7381
+ if (!import_fs10.default.existsSync(import_path9.default.join(dir, "package.json"))) {
7462
7382
  logWarn3(`${label}/package.json not found \u2014 skipping yarn install`);
7463
7383
  return false;
7464
7384
  }
@@ -7474,12 +7394,12 @@ async function yarnInstall(dir, label) {
7474
7394
  spinner.succeed(`${label} dependencies installed`);
7475
7395
  return true;
7476
7396
  }
7477
- var import_fs9, import_path9;
7397
+ var import_fs10, import_path9;
7478
7398
  var init_yarn_install = __esm({
7479
7399
  "src/commands/app/yarn-install.ts"() {
7480
7400
  "use strict";
7481
7401
  init_cjs_shims();
7482
- import_fs9 = __toESM(require("fs"), 1);
7402
+ import_fs10 = __toESM(require("fs"), 1);
7483
7403
  import_path9 = __toESM(require("path"), 1);
7484
7404
  init_spinner3();
7485
7405
  init_utils3();
@@ -7529,7 +7449,6 @@ function buildWebScaffoldArgv(projectName, portOffset, nonInteractive) {
7529
7449
  "--port-offset",
7530
7450
  String(portOffset),
7531
7451
  "--no-git",
7532
- // Install after local @basekit pin rewrite (registry pins 404 until publish)
7533
7452
  "--no-install"
7534
7453
  ];
7535
7454
  if (nonInteractive) {
@@ -7537,6 +7456,14 @@ function buildWebScaffoldArgv(projectName, portOffset, nonInteractive) {
7537
7456
  }
7538
7457
  return args;
7539
7458
  }
7459
+ function stripNestedFrontendGithub(frontendDir) {
7460
+ const nestedGithub = import_path10.default.join(frontendDir, ".github");
7461
+ if (!import_fs11.default.existsSync(nestedGithub)) {
7462
+ return false;
7463
+ }
7464
+ import_fs11.default.rmSync(nestedGithub, { recursive: true, force: true });
7465
+ return true;
7466
+ }
7540
7467
  async function scaffoldFrontend(opts) {
7541
7468
  const { projectName, portOffset, projectRoot, nonInteractive } = opts;
7542
7469
  logStep3("Running create-basekit web (answer the remaining prompts)");
@@ -7548,23 +7475,24 @@ async function scaffoldFrontend(opts) {
7548
7475
  }
7549
7476
  const scaffoldedDir = import_path10.default.join(projectRoot, projectName);
7550
7477
  const frontendDir = import_path10.default.join(projectRoot, "frontend");
7551
- if (import_fs10.default.existsSync(scaffoldedDir)) {
7552
- import_fs10.default.renameSync(scaffoldedDir, frontendDir);
7478
+ if (import_fs11.default.existsSync(scaffoldedDir)) {
7479
+ import_fs11.default.renameSync(scaffoldedDir, frontendDir);
7553
7480
  logOk4(`Renamed ${projectName}/ \u2192 frontend/`);
7554
7481
  }
7555
- applyLocalBasekitPins(projectRoot);
7482
+ if (stripNestedFrontendGithub(frontendDir)) {
7483
+ logOk4("Removed frontend/.github (root CI owns workflows)");
7484
+ }
7556
7485
  await yarnInstall(frontendDir, "frontend");
7557
7486
  logOk4("Frontend scaffolded");
7558
7487
  }
7559
- var import_path10, import_fs10;
7488
+ var import_path10, import_fs11;
7560
7489
  var init_frontend = __esm({
7561
7490
  "src/commands/app/frontend.ts"() {
7562
7491
  "use strict";
7563
7492
  init_cjs_shims();
7564
7493
  import_path10 = __toESM(require("path"), 1);
7565
- import_fs10 = __toESM(require("fs"), 1);
7494
+ import_fs11 = __toESM(require("fs"), 1);
7566
7495
  init_web();
7567
- init_local_basekit_pins();
7568
7496
  init_utils3();
7569
7497
  init_yarn_install();
7570
7498
  init_in_process_cli();
@@ -7605,18 +7533,18 @@ async function scaffoldMobile(opts) {
7605
7533
  const scaffoldedDir = import_path11.default.join(projectRoot, projectName);
7606
7534
  const mobileDir = import_path11.default.join(projectRoot, "mobile");
7607
7535
  if (exists3(scaffoldedDir)) {
7608
- import_fs11.default.renameSync(scaffoldedDir, mobileDir);
7536
+ import_fs12.default.renameSync(scaffoldedDir, mobileDir);
7609
7537
  logOk4(`Renamed ${projectName}/ \u2192 mobile/`);
7610
7538
  }
7611
7539
  return true;
7612
7540
  }
7613
- var import_path11, import_fs11, CREATE_BASEKIT_PKG;
7541
+ var import_path11, import_fs12, CREATE_BASEKIT_PKG;
7614
7542
  var init_mobile2 = __esm({
7615
7543
  "src/commands/app/mobile.ts"() {
7616
7544
  "use strict";
7617
7545
  init_cjs_shims();
7618
7546
  import_path11 = __toESM(require("path"), 1);
7619
- import_fs11 = __toESM(require("fs"), 1);
7547
+ import_fs12 = __toESM(require("fs"), 1);
7620
7548
  init_spinner3();
7621
7549
  init_mobile();
7622
7550
  init_utils3();
@@ -7626,18 +7554,9 @@ var init_mobile2 = __esm({
7626
7554
  });
7627
7555
 
7628
7556
  // src/commands/app/project-identity.ts
7629
- function resolveNewProjectFixturePath(fromDir = import_path12.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl))) {
7630
- const candidates = [
7631
- import_path12.default.join(fromDir, "../../../fixtures/new_project.py"),
7632
- import_path12.default.join(fromDir, "../fixtures/new_project.py")
7633
- ];
7634
- for (const candidate of candidates) {
7635
- const resolved = import_path12.default.resolve(candidate);
7636
- if (import_fs12.default.existsSync(resolved)) return resolved;
7637
- }
7638
- throw new Error(
7639
- `fixtures/new_project.py missing (searched: ${candidates.map((c) => import_path12.default.resolve(c)).join(", ")})`
7640
- );
7557
+ function resolveNewProjectFixturePath(fromDir = import_path12.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl))) {
7558
+ const fixture = import_path12.default.join(getCreateBasekitPackageRoot(fromDir), "fixtures", "new_project.py");
7559
+ return fixture;
7641
7560
  }
7642
7561
  function deriveProjectIdentity(projectName) {
7643
7562
  const slug = toPythonSlug(projectName);
@@ -7659,17 +7578,18 @@ function writeProjectIdentity(projectRoot, projectName) {
7659
7578
  mkdirp(scriptsDir);
7660
7579
  const fixturePath = resolveNewProjectFixturePath();
7661
7580
  const scriptPath = import_path12.default.join(scriptsDir, "new_project.py");
7662
- import_fs12.default.copyFileSync(fixturePath, scriptPath);
7663
- import_fs12.default.chmodSync(scriptPath, 493);
7581
+ import_fs13.default.copyFileSync(fixturePath, scriptPath);
7582
+ import_fs13.default.chmodSync(scriptPath, 493);
7664
7583
  }
7665
- var import_fs12, import_path12, import_node_url3;
7584
+ var import_fs13, import_path12, import_node_url4;
7666
7585
  var init_project_identity = __esm({
7667
7586
  "src/commands/app/project-identity.ts"() {
7668
7587
  "use strict";
7669
7588
  init_cjs_shims();
7670
- import_fs12 = __toESM(require("fs"), 1);
7589
+ import_fs13 = __toESM(require("fs"), 1);
7671
7590
  import_path12 = __toESM(require("path"), 1);
7672
- import_node_url3 = require("url");
7591
+ import_node_url4 = require("url");
7592
+ init_package_paths();
7673
7593
  init_utils3();
7674
7594
  }
7675
7595
  });
@@ -7703,14 +7623,10 @@ function createRootFiles({
7703
7623
  logOk4(".github/");
7704
7624
  mkdirp(import_path13.default.join(projectRoot, ".claude"));
7705
7625
  logOk4(".claude/");
7706
- if (mobile) {
7707
- const workflowsDir = import_path13.default.join(projectRoot, ".github", "workflows");
7708
- mkdirp(workflowsDir);
7709
- writeText3(import_path13.default.join(workflowsDir, "ci.yml"), githubCi(projectName, frontend));
7710
- logOk4(".github/workflows/ci.yml");
7711
- writeText3(import_path13.default.join(projectRoot, ".github", "dependabot.yml"), githubDependabot(frontend));
7712
- logOk4(".github/dependabot.yml");
7713
- }
7626
+ const workflowsDir = import_path13.default.join(projectRoot, ".github", "workflows");
7627
+ mkdirp(workflowsDir);
7628
+ writeText3(import_path13.default.join(workflowsDir, "ci.yml"), githubCi(projectName, frontend, mobile));
7629
+ logOk4(".github/workflows/ci.yml");
7714
7630
  }
7715
7631
  function rootReadme(projectName, portOffset, mobile, frontend = true) {
7716
7632
  const bePort = 8e3 + portOffset;
@@ -8036,7 +7952,7 @@ function rootMakefile(mobile, frontend = true) {
8036
7952
  ` : "";
8037
7953
  const feStop = frontend ? ' @pkill -f "[v]ite" >/dev/null 2>&1 || true\n' : "";
8038
7954
  const feTarget = frontend ? `
8039
- start-fe: ## Start frontend only (host yarn \u2014 works with local @appsbyme file: pins)
7955
+ start-fe: ## Start frontend only (host yarn)
8040
7956
  @cd frontend && yarn dev
8041
7957
  ` : "";
8042
7958
  const setupFe = frontend ? " @cd frontend && yarn install\n" : "";
@@ -8104,32 +8020,95 @@ ${setupTarget}${feTarget}${mobileTargets}stop: ## Stop all running services
8104
8020
  ${feStop}${mobile ? ' @pkill -f "[e]xpo start" >/dev/null 2>&1 || true\n' : ""} @echo "Done."
8105
8021
  `;
8106
8022
  }
8107
- function githubCi(projectName, frontend = true) {
8023
+ function githubCi(projectName, frontend = true, mobile = false) {
8024
+ const dockerNeeds = frontend ? "[backend, frontend]" : "[backend]";
8025
+ const frontendDockerStep = frontend ? `
8026
+ - name: Build frontend image
8027
+ run: docker build -f frontend/Dockerfile -t ${projectName}-fe:\${{ github.sha }} .
8028
+ ` : "";
8029
+ const frontendSharedInstall = mobile ? `
8030
+ - name: Install shared dependencies
8031
+ working-directory: packages/shared
8032
+ run: yarn install --immutable
8033
+ ` : "";
8034
+ const frontendCachePath = mobile ? `|
8035
+ frontend/yarn.lock
8036
+ packages/shared/yarn.lock` : "frontend/yarn.lock";
8108
8037
  const frontendJob = frontend ? `
8109
8038
  frontend:
8110
- name: Frontend (Node)
8039
+ name: "Frontend \u2014 type-check + lint + test"
8111
8040
  runs-on: ubuntu-latest
8112
8041
  defaults:
8113
8042
  run:
8114
8043
  working-directory: frontend
8115
8044
  steps:
8116
8045
  - uses: actions/checkout@v4
8046
+ - name: Enable Corepack (Yarn 4)
8047
+ run: corepack enable
8117
8048
  - uses: actions/setup-node@v4
8118
8049
  with:
8119
- node-version: 20
8120
- - run: corepack enable
8050
+ node-version: 22
8051
+ cache: yarn
8052
+ cache-dependency-path: ${frontendCachePath}
8053
+ ${frontendSharedInstall} - run: yarn install --immutable
8054
+ - run: yarn type-check
8055
+ - run: yarn lint
8056
+ - run: yarn stylelint
8057
+ - run: yarn test:run
8058
+ - run: yarn build
8059
+ ` : "";
8060
+ const mobileJobs = mobile ? `
8061
+ mobile:
8062
+ name: "Mobile \u2014 type-check + lint + test"
8063
+ runs-on: ubuntu-latest
8064
+ defaults:
8065
+ run:
8066
+ working-directory: mobile
8067
+ steps:
8068
+ - uses: actions/checkout@v4
8069
+ - name: Enable Corepack (Yarn 4)
8070
+ run: corepack enable
8071
+ - uses: actions/setup-node@v4
8072
+ with:
8073
+ node-version: 22
8074
+ cache: yarn
8075
+ cache-dependency-path: |
8076
+ mobile/yarn.lock
8077
+ packages/shared/yarn.lock
8121
8078
  - run: yarn install --immutable
8079
+ - name: Install shared dependencies
8080
+ working-directory: packages/shared
8081
+ run: yarn install --immutable
8082
+ - run: yarn type-check
8122
8083
  - run: yarn lint
8123
- - run: npx tsc --noEmit
8124
- - run: yarn test --run
8084
+ - run: yarn test --watchman=false
8085
+
8086
+ shared:
8087
+ name: "Shared \u2014 type-check + test"
8088
+ runs-on: ubuntu-latest
8089
+ defaults:
8090
+ run:
8091
+ working-directory: packages/shared
8092
+ steps:
8093
+ - uses: actions/checkout@v4
8094
+ - name: Enable Corepack (Yarn 4)
8095
+ run: corepack enable
8096
+ - uses: actions/setup-node@v4
8097
+ with:
8098
+ node-version: 22
8099
+ cache: yarn
8100
+ cache-dependency-path: packages/shared/yarn.lock
8101
+ - run: yarn install --immutable
8102
+ - run: yarn type-check
8103
+ - run: yarn test
8125
8104
  ` : "";
8126
8105
  return `name: CI
8127
8106
 
8128
8107
  on:
8129
8108
  push:
8130
- branches: [main]
8109
+ branches: [master]
8131
8110
  pull_request:
8132
- branches: [main]
8111
+ branches: [master]
8133
8112
 
8134
8113
  concurrency:
8135
8114
  group: \${{ github.workflow }}-\${{ github.ref }}
@@ -8137,94 +8116,41 @@ concurrency:
8137
8116
 
8138
8117
  jobs:
8139
8118
  backend:
8140
- name: Backend (Python)
8119
+ name: "Backend \u2014 lint + test"
8141
8120
  runs-on: ubuntu-latest
8142
8121
  defaults:
8143
8122
  run:
8144
8123
  working-directory: backend
8145
- services:
8146
- postgres:
8147
- image: postgres:16
8148
- env:
8149
- POSTGRES_USER: ci
8150
- POSTGRES_PASSWORD: ci
8151
- POSTGRES_DB: ${projectName.replace(/-/g, "_")}_test
8152
- ports: ["5432:5432"]
8153
- options: >-
8154
- --health-cmd pg_isready
8155
- --health-interval 10s
8156
- --health-timeout 5s
8157
- --health-retries 5
8158
- redis:
8159
- image: redis:7
8160
- ports: ["6379:6379"]
8161
8124
  steps:
8162
8125
  - uses: actions/checkout@v4
8163
8126
  - uses: actions/setup-python@v5
8164
8127
  with:
8165
8128
  python-version: "3.12"
8166
- - run: pip install -e ".[dev,test]"
8129
+ cache: pip
8130
+ cache-dependency-path: backend/requirements*.txt
8131
+ - name: Install dependencies
8132
+ run: |
8133
+ python -m pip install --upgrade pip
8134
+ pip install -r requirements.txt -r requirements-dev.txt
8167
8135
  - run: ruff check .
8168
8136
  - run: ruff format --check .
8169
- - run: pytest --tb=short -q
8170
- ${frontendJob}
8171
- mobile:
8172
- name: Mobile (Expo)
8173
- runs-on: ubuntu-latest
8174
- defaults:
8175
- run:
8176
- working-directory: mobile
8177
- steps:
8178
- - uses: actions/checkout@v4
8179
- - uses: actions/setup-node@v4
8180
- with:
8181
- node-version: 20
8182
- - run: yarn install
8183
- - run: npx tsc --noEmit
8184
- - run: yarn test --ci
8185
-
8186
- shared:
8187
- name: Shared packages
8137
+ - name: Run tests
8138
+ env:
8139
+ DJANGO_SETTINGS_MODULE: config.settings.test
8140
+ SECRET_KEY: ci-test-secret-key-not-for-production
8141
+ CELERY_TASK_ALWAYS_EAGER: "True"
8142
+ run: python -m pytest --tb=short -q
8143
+ ${frontendJob}${mobileJobs}
8144
+ docker:
8145
+ name: "Docker \u2014 smoke build"
8188
8146
  runs-on: ubuntu-latest
8189
- defaults:
8190
- run:
8191
- working-directory: packages/shared
8147
+ needs: ${dockerNeeds}
8148
+ if: github.ref == 'refs/heads/master'
8192
8149
  steps:
8193
8150
  - uses: actions/checkout@v4
8194
- - uses: actions/setup-node@v4
8195
- with:
8196
- node-version: 20
8197
- - run: npx tsc --noEmit
8198
- `;
8199
- }
8200
- function githubDependabot(frontend = true) {
8201
- const frontendUpdate = frontend ? `
8202
- - package-ecosystem: npm
8203
- directory: /frontend
8204
- schedule:
8205
- interval: weekly
8206
- open-pull-requests-limit: 10
8207
- ` : "";
8208
- return `version: 2
8209
- updates:
8210
- - package-ecosystem: pip
8211
- directory: /backend
8212
- schedule:
8213
- interval: weekly
8214
- open-pull-requests-limit: 10
8215
- ${frontendUpdate}
8216
- - package-ecosystem: npm
8217
- directory: /mobile
8218
- schedule:
8219
- interval: weekly
8220
- open-pull-requests-limit: 10
8221
-
8222
- - package-ecosystem: github-actions
8223
- directory: /
8224
- schedule:
8225
- interval: weekly
8226
- open-pull-requests-limit: 5
8227
- `;
8151
+ - name: Build backend image
8152
+ run: docker build -t ${projectName}-be:\${{ github.sha }} ./backend
8153
+ ${frontendDockerStep}`;
8228
8154
  }
8229
8155
  var import_path13;
8230
8156
  var init_root_files = __esm({
@@ -8251,6 +8177,8 @@ function scaffoldSharedPackage(projectRoot, projectName) {
8251
8177
  mkdirp(import_path14.default.join(sharedRoot, "src", "i18n"));
8252
8178
  writeText3(import_path14.default.join(sharedRoot, "package.json"), sharedPackageJson(projectName));
8253
8179
  logOk4("package.json");
8180
+ writeText3(import_path14.default.join(sharedRoot, ".yarnrc.yml"), "nodeLinker: node-modules\n");
8181
+ logOk4(".yarnrc.yml");
8254
8182
  writeText3(import_path14.default.join(sharedRoot, "tsconfig.json"), sharedTsconfig());
8255
8183
  logOk4("tsconfig.json");
8256
8184
  writeText3(import_path14.default.join(sharedRoot, "vitest.config.ts"), sharedVitestConfig());
@@ -8590,10 +8518,14 @@ var init_shared_package = __esm({
8590
8518
  // src/commands/app/shared-patch.ts
8591
8519
  function patchSharedReferences(projectRoot) {
8592
8520
  logStep3("Patching shared package references");
8593
- patchFrontendDockerfile(projectRoot);
8521
+ patchFrontendDocker(projectRoot);
8594
8522
  verifyFrontendSharedAlias(projectRoot);
8595
8523
  verifyMobileSharedPaths(projectRoot);
8596
8524
  }
8525
+ function patchFrontendDocker(projectRoot) {
8526
+ patchFrontendDockerfile(projectRoot);
8527
+ patchFrontendCompose(projectRoot);
8528
+ }
8597
8529
  function patchFrontendDockerfile(projectRoot) {
8598
8530
  const dockerfilePath = import_path15.default.join(projectRoot, "frontend", "Dockerfile");
8599
8531
  if (!exists3(dockerfilePath)) {
@@ -8601,30 +8533,102 @@ function patchFrontendDockerfile(projectRoot) {
8601
8533
  return;
8602
8534
  }
8603
8535
  let content = readText3(dockerfilePath);
8604
- if (content.includes("COPY packages/shared/")) {
8605
- logOk4("Dockerfile \u2014 packages/shared/ COPY already present");
8536
+ if (content.includes("WORKDIR /workspace/packages/shared") && content.includes("COPY packages/shared/") && content.includes("COPY frontend/")) {
8537
+ logOk4("Dockerfile \u2014 monorepo shared COPY + install already present");
8606
8538
  return;
8607
8539
  }
8608
- const manifestCopyPatterns = [
8609
- /^(COPY package\.json \.yarnrc\.yml \.\/\n)/m,
8610
- /^(COPY package\.json yarn\.lock \.yarnrc\.yml \.\/\n)/m
8611
- ];
8612
- const matched = manifestCopyPatterns.find((pattern) => pattern.test(content));
8613
- if (matched) {
8614
- content = content.replace(matched, "$1COPY packages/shared/ packages/shared/\n");
8615
- content = content.replace(
8616
- /# Root manifests and lockfile are copied first so `yarn install` is only\n# re-run when dependencies change, not on every source file change\.\n/,
8617
- "# Root manifests, lockfile, and shared packages are copied first so\n# `yarn install` is only re-run when dependencies change.\n"
8618
- );
8540
+ if (content.includes("COPY packages/shared/") && content.includes("WORKDIR /workspace/frontend") && !content.includes("WORKDIR /workspace/packages/shared")) {
8619
8541
  content = content.replace(
8620
- /# Copy dependency files first so dependency installation is cached separately\.\n/,
8621
- "# Copy dependency files and shared packages first so installs cache separately.\n"
8542
+ "WORKDIR /workspace/frontend\nRUN --mount=type=cache,target=/root/.yarn/berry/cache \\\n yarn install --immutable",
8543
+ [
8544
+ "# Shared must have its own node_modules (zod, etc.) \u2014 tsc resolves from source.",
8545
+ "WORKDIR /workspace/packages/shared",
8546
+ "RUN --mount=type=cache,target=/root/.yarn/berry/cache \\",
8547
+ " yarn install --immutable",
8548
+ "",
8549
+ "WORKDIR /workspace/frontend",
8550
+ "RUN --mount=type=cache,target=/root/.yarn/berry/cache \\",
8551
+ " yarn install --immutable"
8552
+ ].join("\n")
8622
8553
  );
8623
8554
  writeText3(dockerfilePath, content);
8624
- logOk4("Dockerfile \u2014 added COPY packages/shared/ for Docker builds");
8555
+ logOk4("Dockerfile \u2014 added packages/shared yarn install for Docker builds");
8556
+ return;
8557
+ }
8558
+ const builderCopy = [
8559
+ "WORKDIR /workspace",
8560
+ "",
8561
+ "RUN corepack enable",
8562
+ "",
8563
+ "COPY packages/shared/ packages/shared/",
8564
+ "COPY frontend/package.json frontend/yarn.lock frontend/.yarnrc.yml frontend/",
8565
+ "",
8566
+ "# Shared must have its own node_modules (zod, etc.) \u2014 tsc resolves from source.",
8567
+ "WORKDIR /workspace/packages/shared",
8568
+ "RUN --mount=type=cache,target=/root/.yarn/berry/cache \\",
8569
+ " yarn install --immutable",
8570
+ "",
8571
+ "WORKDIR /workspace/frontend",
8572
+ "RUN --mount=type=cache,target=/root/.yarn/berry/cache \\",
8573
+ " yarn install --immutable",
8574
+ "",
8575
+ "COPY frontend/ .",
8576
+ "RUN yarn build"
8577
+ ].join("\n");
8578
+ const replaced = content.replace(
8579
+ /WORKDIR \/app[\s\S]*?RUN yarn build/,
8580
+ builderCopy
8581
+ );
8582
+ if (replaced === content) {
8583
+ const replaced2 = content.replace(
8584
+ /# Set working directory\nWORKDIR \/app[\s\S]*?RUN yarn build/,
8585
+ `# Monorepo root is the build context (see docker-compose app-prod).
8586
+ ${builderCopy}`
8587
+ );
8588
+ if (replaced2 === content) {
8589
+ logWarn3("Dockerfile \u2014 could not rewrite builder stage; patch skipped");
8590
+ return;
8591
+ }
8592
+ content = replaced2;
8625
8593
  } else {
8626
- logWarn3("Dockerfile \u2014 could not locate manifest COPY line; patch skipped");
8594
+ content = replaced;
8627
8595
  }
8596
+ content = content.replace(
8597
+ /^COPY nginx\.conf /m,
8598
+ "COPY frontend/nginx.conf "
8599
+ );
8600
+ content = content.replace(
8601
+ /^COPY nginx\//m,
8602
+ "COPY frontend/nginx/"
8603
+ );
8604
+ content = content.replace(
8605
+ /COPY --from=builder \/app\/dist /,
8606
+ "COPY --from=builder /workspace/frontend/dist "
8607
+ );
8608
+ writeText3(dockerfilePath, content);
8609
+ logOk4("Dockerfile \u2014 switched to monorepo root context with packages/shared");
8610
+ }
8611
+ function patchFrontendCompose(projectRoot) {
8612
+ const composePath = import_path15.default.join(projectRoot, "frontend", "docker-compose.yml");
8613
+ if (!exists3(composePath)) {
8614
+ logWarn3("Frontend docker-compose.yml not found \u2014 skipping compose patch");
8615
+ return;
8616
+ }
8617
+ let content = readText3(composePath);
8618
+ if (content.includes("context: ..") && content.includes("dockerfile: frontend/Dockerfile")) {
8619
+ logOk4("docker-compose.yml \u2014 monorepo build context already set");
8620
+ return;
8621
+ }
8622
+ const next = content.replace(
8623
+ /app-prod:\n(\s+)build:\n\s+context: \.\n\s+dockerfile: Dockerfile/,
8624
+ "app-prod:\n$1build:\n$1 context: ..\n$1 dockerfile: frontend/Dockerfile"
8625
+ );
8626
+ if (next === content) {
8627
+ logWarn3("docker-compose.yml \u2014 could not locate app-prod build block; patch skipped");
8628
+ return;
8629
+ }
8630
+ writeText3(composePath, next);
8631
+ logOk4("docker-compose.yml \u2014 app-prod build context set to repo root");
8628
8632
  }
8629
8633
  function verifyFrontendSharedAlias(projectRoot) {
8630
8634
  const tsconfigPath = import_path15.default.join(projectRoot, "frontend", "tsconfig.json");
@@ -8699,7 +8703,7 @@ function getAiHelpersRetryHint(target) {
8699
8703
  const packageJson = require2.resolve(AI_HELPERS_PACKAGE_JSON);
8700
8704
  binPath = import_path16.default.join(import_path16.default.dirname(packageJson), "bin", "cli.mjs");
8701
8705
  } catch {
8702
- return "Install/link the basekit-web workspace dependencies, then re-run create-basekit app with --ai-helpers.";
8706
+ return `Install with \`npm i -D ${AI_HELPERS_PKG}\` (or \`npx ${AI_HELPERS_PKG}\`), then re-run create-basekit app with --ai-helpers.`;
8703
8707
  }
8704
8708
  }
8705
8709
  return `node ${JSON.stringify(binPath)} ${JSON.stringify(target)} --with-claude`;
@@ -8746,7 +8750,7 @@ async function runAppCommand(argv) {
8746
8750
  return;
8747
8751
  }
8748
8752
  if (args.includes("--version") || args.includes("-V")) {
8749
- console.log("1.0.3");
8753
+ console.log("1.0.5");
8750
8754
  return;
8751
8755
  }
8752
8756
  printBanner3();
@@ -8853,7 +8857,7 @@ async function runAppCommand(argv) {
8853
8857
  logError3(err instanceof Error ? err.message : String(err));
8854
8858
  if (exists3(projectRoot)) {
8855
8859
  try {
8856
- import_fs13.default.rmSync(projectRoot, { recursive: true, force: true });
8860
+ import_fs14.default.rmSync(projectRoot, { recursive: true, force: true });
8857
8861
  logInfo2(`Cleaned up ${projectName}/`);
8858
8862
  } catch {
8859
8863
  logError3(`Could not clean up ${projectName}/ \u2014 remove it manually before retrying.`);
@@ -8880,12 +8884,12 @@ function initRootGit(projectRoot, includeMobile = false) {
8880
8884
  logInfo2("git not found \u2014 skipping repository initialisation");
8881
8885
  return;
8882
8886
  }
8883
- const fs14 = require("fs");
8887
+ const fs15 = require("fs");
8884
8888
  const subDirs = includeMobile ? ["backend", "frontend", "mobile"] : ["backend", "frontend"];
8885
8889
  for (const sub of subDirs) {
8886
8890
  const gitDir = import_path17.default.join(projectRoot, sub, ".git");
8887
8891
  if (exists3(gitDir)) {
8888
- fs14.rmSync(gitDir, { recursive: true, force: true });
8892
+ fs15.rmSync(gitDir, { recursive: true, force: true });
8889
8893
  }
8890
8894
  }
8891
8895
  const init = exec3("git init", projectRoot);
@@ -8908,7 +8912,7 @@ function initRootGit(projectRoot, includeMobile = false) {
8908
8912
  function printBanner3() {
8909
8913
  console.log("");
8910
8914
  console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
8911
- console.log(` \u2551 create-basekit app v${"1.0.3"} \u2551`);
8915
+ console.log(` \u2551 create-basekit app v${"1.0.5"} \u2551`);
8912
8916
  console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
8913
8917
  console.log("");
8914
8918
  console.log(" Full-stack project scaffolder");
@@ -8916,7 +8920,7 @@ function printBanner3() {
8916
8920
  }
8917
8921
  function printHelp3() {
8918
8922
  console.log(`
8919
- create-basekit app v${"1.0.3"}
8923
+ create-basekit app v${"1.0.5"}
8920
8924
 
8921
8925
  Usage: npx @appsbyme/create-basekit app [project-name] [options]
8922
8926
 
@@ -9017,7 +9021,7 @@ function printSummary(projectName, portOffset, aiHelpers = false, mobile = false
9017
9021
  }
9018
9022
  console.log("");
9019
9023
  }
9020
- var import_path17, import_prompts3, import_fs13;
9024
+ var import_path17, import_prompts3, import_fs14;
9021
9025
  var init_app = __esm({
9022
9026
  "src/commands/app/index.ts"() {
9023
9027
  "use strict";
@@ -9032,7 +9036,7 @@ var init_app = __esm({
9032
9036
  init_shared_package();
9033
9037
  init_shared_patch();
9034
9038
  init_yarn_install();
9035
- import_fs13 = __toESM(require("fs"), 1);
9039
+ import_fs14 = __toESM(require("fs"), 1);
9036
9040
  init_utils3();
9037
9041
  init_spinner3();
9038
9042
  init_ai_helpers();
@@ -9087,7 +9091,7 @@ function printHelp4() {
9087
9091
  console.log("");
9088
9092
  }
9089
9093
  function printVersion() {
9090
- console.log("1.0.3");
9094
+ console.log("1.0.5");
9091
9095
  }
9092
9096
  async function main() {
9093
9097
  const argv = process.argv.slice(2);