@atlashub/smartstack-cli 4.58.0 → 4.60.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 CHANGED
@@ -113279,13 +113279,18 @@ async function installRalphConfig(options = {}) {
113279
113279
  result.errors.push("Ralph config template not found");
113280
113280
  logger.warning("Ralph config template not found in package");
113281
113281
  }
113282
- const gitignoreSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", ".gitignore");
113282
+ const gitignoreSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", "gitignore.template");
113283
113283
  const gitignoreDest = (0, import_path2.join)(ralphDir, ".gitignore");
113284
113284
  if (await import_fs_extra2.default.pathExists(gitignoreSrc)) {
113285
113285
  if (!await import_fs_extra2.default.pathExists(gitignoreDest) || options.force) {
113286
113286
  const content = await import_fs_extra2.default.readFile(gitignoreSrc);
113287
113287
  await import_fs_extra2.default.writeFile(gitignoreDest, content);
113288
+ logger.success("Created .ralph/.gitignore");
113289
+ } else {
113290
+ logger.info(".ralph/.gitignore already exists (use --force to overwrite)");
113288
113291
  }
113292
+ } else {
113293
+ logger.warning(".ralph/gitignore.template not found in package \u2014 .gitignore not created");
113289
113294
  }
113290
113295
  const readmeSrc = (0, import_path2.join)(TEMPLATES_DIR, "ralph", "README.md");
113291
113296
  const readmeDest = (0, import_path2.join)(ralphDir, "README.md");
@@ -117199,7 +117204,7 @@ ${projectName}/
117199
117204
  \u2502 \u2514\u2500\u2500 ${projectName}.Api/ # Web API controllers and configuration
117200
117205
  \u251C\u2500\u2500 web/
117201
117206
  \u2502 \u2514\u2500\u2500 ${projectName.toLowerCase()}-web/ # React frontend (Vite + TypeScript + Tailwind)
117202
- \u251C\u2500\u2500 docker-images/ # Dockerfiles (backend + frontend)
117207
+ \u251C\u2500\u2500 docker-images/ # Dockerfiles + docker-compose (backend + frontend)
117203
117208
  \u2514\u2500\u2500 tests/ # Unit and integration tests
117204
117209
  \`\`\`
117205
117210
 
@@ -117316,6 +117321,7 @@ async function createFrontendStructure(config, state, dryRun) {
117316
117321
  "eslint-plugin-react-refresh": "^0.4.24",
117317
117322
  globals: "^16.5.0",
117318
117323
  jsdom: "^28.0.0",
117324
+ msw: "^2.7.5",
117319
117325
  typescript: "~5.9.3",
117320
117326
  "typescript-eslint": "^8.46.4",
117321
117327
  vite: "^7.2.4",
@@ -117734,6 +117740,19 @@ async function createDockerFiles(config, state, dryRun) {
117734
117740
  logSafeWriteResult(dockerignoreRelPath, dockerignoreResult);
117735
117741
  recordFile(state, "docker", dockerignoreRelPath, dockerignoreResult.hash);
117736
117742
  }
117743
+ const dockerCompose = await loadTemplate("docker-compose.yml.template", projectName);
117744
+ const composeRelPath = "docker-images/docker-compose.yml";
117745
+ if (dryRun) {
117746
+ logger.info(`Would create ${composeRelPath}`);
117747
+ } else {
117748
+ const composeResult = await safeWriteFile(
117749
+ (0, import_path7.join)(projectDir, composeRelPath),
117750
+ dockerCompose,
117751
+ findKnownHash(state, "docker", composeRelPath)
117752
+ );
117753
+ logSafeWriteResult(composeRelPath, composeResult);
117754
+ recordFile(state, "docker", composeRelPath, composeResult.hash);
117755
+ }
117737
117756
  logger.success("Docker configuration created");
117738
117757
  }
117739
117758
  function checkGitIdentity(cwd) {
@@ -118134,6 +118153,17 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
118134
118153
  const ralphResult = await installRalphConfig({ projectPath: finalProjectDir });
118135
118154
  if (ralphResult.success) {
118136
118155
  logger.success("Ralph configuration created at .ralph/");
118156
+ const ralphFiles = [
118157
+ { path: ".ralph/ralph.config.yaml", abs: ralphResult.configPath },
118158
+ { path: ".ralph/.gitignore", abs: (0, import_path7.join)(finalProjectDir, ".ralph", ".gitignore") },
118159
+ { path: ".ralph/README.md", abs: (0, import_path7.join)(finalProjectDir, ".ralph", "README.md") }
118160
+ ];
118161
+ for (const rf of ralphFiles) {
118162
+ if (await import_fs_extra6.default.pathExists(rf.abs)) {
118163
+ const content = await import_fs_extra6.default.readFile(rf.abs);
118164
+ recordFile(state, "ralph", rf.path, computeHash(content));
118165
+ }
118166
+ }
118137
118167
  } else {
118138
118168
  logger.warning("Could not create Ralph configuration (non-critical)");
118139
118169
  }
@@ -118168,6 +118198,7 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
118168
118198
  ` 3. Start dev server: ${source_default.cyan("npm run dev")}`,
118169
118199
  "",
118170
118200
  source_default.yellow("Docker:"),
118201
+ ` Build & run: ${source_default.cyan("docker compose -f docker-images/docker-compose.yml up --build")}`,
118171
118202
  ` Build backend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-backend:latest -f docker-images/Dockerfile.backend .`)}`,
118172
118203
  ` Build frontend: ${source_default.cyan(`docker build -t smartstack-${projectNameLower}-frontend:latest -f docker-images/Dockerfile.frontend .`)}`,
118173
118204
  "",
@@ -125275,25 +125306,97 @@ async function syncClaudeSettings(projectDir, dryRun) {
125275
125306
  }
125276
125307
  return added;
125277
125308
  }
125309
+ async function syncTestFrontendTemplates(frontendDir, dryRun) {
125310
+ const testFrontendDir = (0, import_path8.join)(TEMPLATES_DIR3, "test-frontend");
125311
+ if (!await import_fs_extra7.default.pathExists(testFrontendDir)) {
125312
+ logger.debug("test-frontend templates not found, skipping");
125313
+ return 0;
125314
+ }
125315
+ let synced = 0;
125316
+ const testFiles = [
125317
+ { src: "vitest.config.ts", dest: "vitest.config.ts" },
125318
+ { src: "setup.ts", dest: (0, import_path8.join)("src", "test", "setup.ts") },
125319
+ { src: "test-utils.tsx", dest: (0, import_path8.join)("src", "test", "test-utils.tsx") },
125320
+ { src: "msw/handlers.ts", dest: (0, import_path8.join)("src", "test", "msw", "handlers.ts") },
125321
+ { src: "msw/server.ts", dest: (0, import_path8.join)("src", "test", "msw", "server.ts") }
125322
+ ];
125323
+ for (const file of testFiles) {
125324
+ const destPath = (0, import_path8.join)(frontendDir, file.dest);
125325
+ if (await import_fs_extra7.default.pathExists(destPath)) continue;
125326
+ const srcPath = (0, import_path8.join)(testFrontendDir, file.src);
125327
+ if (!await import_fs_extra7.default.pathExists(srcPath)) continue;
125328
+ if (dryRun) {
125329
+ logger.warning(`[DRY RUN] Would create ${file.dest}`);
125330
+ } else {
125331
+ await import_fs_extra7.default.ensureDir((0, import_path8.dirname)(destPath));
125332
+ const content = await import_fs_extra7.default.readFile(srcPath, "utf-8");
125333
+ await import_fs_extra7.default.writeFile(destPath, content, "utf-8");
125334
+ logger.info(` ${source_default.green("+")} ${file.dest}`);
125335
+ }
125336
+ synced++;
125337
+ }
125338
+ return synced;
125339
+ }
125340
+ async function cleanResidualFiles(projectDir, baseNamespace, dryRun) {
125341
+ let cleaned = 0;
125342
+ const residuals = [
125343
+ (0, import_path8.join)(projectDir, "src", `${baseNamespace}.Api`, `${baseNamespace}.Api.http`)
125344
+ ];
125345
+ for (const file of residuals) {
125346
+ if (await import_fs_extra7.default.pathExists(file)) {
125347
+ if (dryRun) {
125348
+ logger.warning(`[DRY RUN] Would remove residual: ${file}`);
125349
+ } else {
125350
+ await import_fs_extra7.default.remove(file);
125351
+ logger.info(` ${source_default.red("\u2212")} Removed residual: ${baseNamespace}.Api.http`);
125352
+ }
125353
+ cleaned++;
125354
+ }
125355
+ }
125356
+ return cleaned;
125357
+ }
125358
+ async function fixInitStateVersion(projectDir, cliVersion, dryRun) {
125359
+ const statePath = (0, import_path8.join)(projectDir, ".smartstack", "init-state.json");
125360
+ if (!await import_fs_extra7.default.pathExists(statePath)) return false;
125361
+ try {
125362
+ const state = await import_fs_extra7.default.readJson(statePath);
125363
+ if (state.cliVersion === "0.0.0") {
125364
+ if (dryRun) {
125365
+ logger.warning("[DRY RUN] Would fix cliVersion 0.0.0 in init-state.json");
125366
+ } else {
125367
+ state.cliVersion = cliVersion;
125368
+ await import_fs_extra7.default.writeJson(statePath, state, { spaces: 2 });
125369
+ logger.info(` ${source_default.green("+")} Fixed cliVersion: 0.0.0 \u2192 ${cliVersion}`);
125370
+ }
125371
+ return true;
125372
+ }
125373
+ } catch {
125374
+ }
125375
+ return false;
125376
+ }
125278
125377
  async function syncDockerFiles(projectDir, baseNamespace, dryRun) {
125279
125378
  const projectName = baseNamespace;
125280
- const projectNameLower = projectName.toLowerCase();
125281
125379
  let updated = 0;
125282
125380
  const files = [
125283
125381
  {
125284
125382
  template: "Dockerfile.backend.template",
125285
- dest: (0, import_path8.join)(projectDir, "src", `${projectName}.Api`, "Dockerfile"),
125286
- label: `src/${projectName}.Api/Dockerfile`
125383
+ dest: (0, import_path8.join)(projectDir, "docker-images", "Dockerfile.backend"),
125384
+ label: "docker-images/Dockerfile.backend"
125287
125385
  },
125288
125386
  {
125289
125387
  template: "Dockerfile.frontend.template",
125290
- dest: (0, import_path8.join)(projectDir, "web", `${projectNameLower}-web`, "Dockerfile"),
125291
- label: `web/${projectNameLower}-web/Dockerfile`
125388
+ dest: (0, import_path8.join)(projectDir, "docker-images", "Dockerfile.frontend"),
125389
+ label: "docker-images/Dockerfile.frontend"
125292
125390
  },
125293
125391
  {
125294
125392
  template: "dockerignore.template",
125295
125393
  dest: (0, import_path8.join)(projectDir, ".dockerignore"),
125296
125394
  label: ".dockerignore"
125395
+ },
125396
+ {
125397
+ template: "docker-compose.yml.template",
125398
+ dest: (0, import_path8.join)(projectDir, "docker-images", "docker-compose.yml"),
125399
+ label: "docker-images/docker-compose.yml"
125297
125400
  }
125298
125401
  ];
125299
125402
  for (const file of files) {
@@ -125342,6 +125445,9 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125342
125445
  ralphInitialized: false,
125343
125446
  ralphUpToDate: false,
125344
125447
  dockerSynced: 0,
125448
+ testFrontendSynced: 0,
125449
+ residualsCleaned: 0,
125450
+ initStateFixed: false,
125345
125451
  programCsIssues: []
125346
125452
  };
125347
125453
  if (dryRun) {
@@ -125525,6 +125631,14 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125525
125631
  } else {
125526
125632
  logger.info(`Other npm packages: ${source_default.gray("skipped")} (use ${source_default.cyan("--all-packages")} to upgrade all)`);
125527
125633
  }
125634
+ logger.info("Syncing test-frontend templates...");
125635
+ const testSynced = await syncTestFrontendTemplates(frontendDir, dryRun);
125636
+ result.testFrontendSynced = testSynced;
125637
+ if (testSynced > 0) {
125638
+ logger.success(`${testSynced} test template(s) deployed`);
125639
+ } else {
125640
+ logger.info(`Test templates ${source_default.green("\u2713")} up to date`);
125641
+ }
125528
125642
  console.log();
125529
125643
  }
125530
125644
  logger.info("Checking for code migrations...");
@@ -125595,6 +125709,17 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125595
125709
  logger.info(`Dockerfiles ${source_default.green("\u2713")} up to date`);
125596
125710
  }
125597
125711
  console.log();
125712
+ const residualsCleaned = await cleanResidualFiles(projectDir, config.baseNamespace, dryRun);
125713
+ result.residualsCleaned = residualsCleaned;
125714
+ if (residualsCleaned > 0) {
125715
+ logger.success(`${residualsCleaned} residual file(s) cleaned`);
125716
+ console.log();
125717
+ }
125718
+ result.initStateFixed = await fixInitStateVersion(projectDir, nugetVersion, dryRun);
125719
+ if (result.initStateFixed) {
125720
+ logger.success("Fixed cliVersion in init-state.json");
125721
+ console.log();
125722
+ }
125598
125723
  logger.info("Checking Ralph configuration...");
125599
125724
  const ralphStatus = await checkRalphInstallation(projectDir);
125600
125725
  if (ralphStatus.configExists) {
@@ -125638,7 +125763,7 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125638
125763
  logger.success(`Updated config version to ${source_default.cyan(nugetVersion)}`);
125639
125764
  console.log();
125640
125765
  }
125641
- const totalChanged = result.nugetUpgraded + result.otherPkgUpdated + (result.npmUpgraded ? 1 : 0) + (result.npmOtherUpdated ? 1 : 0) + migrationSummary.totalApplied + result.configSynced + result.claudeSettingsSynced + result.dockerSynced + (result.ralphInitialized ? 1 : 0);
125766
+ const totalChanged = result.nugetUpgraded + result.otherPkgUpdated + (result.npmUpgraded ? 1 : 0) + (result.npmOtherUpdated ? 1 : 0) + migrationSummary.totalApplied + result.configSynced + result.claudeSettingsSynced + result.dockerSynced + result.testFrontendSynced + result.residualsCleaned + (result.initStateFixed ? 1 : 0) + (result.ralphInitialized ? 1 : 0);
125642
125767
  const allUpToDate = totalChanged === 0 && result.nugetFailed === 0 && result.otherPkgFailed === 0;
125643
125768
  const hasProgramIssues = result.programCsIssues.length > 0;
125644
125769
  if (allUpToDate && !hasProgramIssues) {
@@ -125702,6 +125827,15 @@ var upgradeCommand = new Command("upgrade").description("Upgrade SmartStack pack
125702
125827
  if (result.dockerSynced > 0) {
125703
125828
  lines.push(` ${source_default.green("\u2713")} Docker: ${result.dockerSynced} Dockerfile(s) updated`);
125704
125829
  }
125830
+ if (result.testFrontendSynced > 0) {
125831
+ lines.push(` ${source_default.green("\u2713")} Test templates: ${result.testFrontendSynced} file(s) deployed`);
125832
+ }
125833
+ if (result.residualsCleaned > 0) {
125834
+ lines.push(` ${source_default.green("\u2713")} Cleanup: ${result.residualsCleaned} residual file(s) removed`);
125835
+ }
125836
+ if (result.initStateFixed) {
125837
+ lines.push(` ${source_default.green("\u2713")} Init-state: cliVersion fixed`);
125838
+ }
125705
125839
  if (result.ralphInitialized) {
125706
125840
  lines.push(` ${source_default.green("\u2713")} Ralph: configuration initialized`);
125707
125841
  }