@expressots/cli 4.0.0-preview.2 → 4.0.0-preview.3

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.
Files changed (63) hide show
  1. package/bin/cicd/cli.d.ts +1 -1
  2. package/bin/cicd/cli.js +3 -1
  3. package/bin/cicd/form.js +5 -4
  4. package/bin/cli.d.ts +1 -5
  5. package/bin/cli.js +56 -6
  6. package/bin/commands/project.commands.js +233 -26
  7. package/bin/containerize/cli.d.ts +1 -1
  8. package/bin/containerize/cli.js +1 -1
  9. package/bin/containerize/form.js +49 -51
  10. package/bin/containerize/generators/ci-generator.js +16 -12
  11. package/bin/containerize/generators/docker-compose-generator.js +3 -2
  12. package/bin/containerize/generators/dockerfile-generator.js +50 -28
  13. package/bin/containerize/generators/kubernetes-generator.js +5 -4
  14. package/bin/costs/cli.d.ts +1 -1
  15. package/bin/costs/cli.js +4 -2
  16. package/bin/dev/cli.d.ts +1 -1
  17. package/bin/dev/cli.js +3 -1
  18. package/bin/generate/cli.d.ts +1 -1
  19. package/bin/generate/templates/nonopinionated/config.tpl +12 -12
  20. package/bin/generate/templates/nonopinionated/event.tpl +10 -10
  21. package/bin/generate/templates/nonopinionated/guard.tpl +18 -18
  22. package/bin/generate/templates/nonopinionated/handler.tpl +12 -12
  23. package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -27
  24. package/bin/generate/templates/opinionated/config.tpl +47 -47
  25. package/bin/generate/templates/opinionated/event.tpl +15 -15
  26. package/bin/generate/templates/opinionated/guard.tpl +41 -41
  27. package/bin/generate/templates/opinionated/handler.tpl +23 -23
  28. package/bin/generate/templates/opinionated/interceptor.tpl +50 -50
  29. package/bin/generate/utils/command-utils.d.ts +13 -2
  30. package/bin/generate/utils/command-utils.js +50 -17
  31. package/bin/generate/utils/opinionated-cmd.js +19 -12
  32. package/bin/help/cli.d.ts +1 -1
  33. package/bin/help/command-help-registry.d.ts +23 -0
  34. package/bin/help/command-help-registry.js +303 -0
  35. package/bin/help/command-help.d.ts +36 -0
  36. package/bin/help/command-help.js +56 -0
  37. package/bin/help/form.js +127 -30
  38. package/bin/help/main-help.d.ts +8 -0
  39. package/bin/help/main-help.js +126 -0
  40. package/bin/help/render.d.ts +32 -0
  41. package/bin/help/render.js +46 -0
  42. package/bin/info/cli.d.ts +1 -1
  43. package/bin/info/form.d.ts +1 -1
  44. package/bin/info/form.js +11 -11
  45. package/bin/migrate/cli.d.ts +1 -1
  46. package/bin/migrate/cli.js +3 -1
  47. package/bin/migrate/form.js +4 -3
  48. package/bin/new/cli.d.ts +5 -1
  49. package/bin/new/cli.js +62 -14
  50. package/bin/new/form.d.ts +3 -1
  51. package/bin/new/form.js +338 -23
  52. package/bin/profile/cli.d.ts +1 -1
  53. package/bin/profile/cli.js +3 -1
  54. package/bin/profile/form.js +5 -4
  55. package/bin/providers/create/form.js +53 -4
  56. package/bin/studio/cli.js +9 -3
  57. package/bin/templates/cli.js +7 -5
  58. package/bin/utils/add-module-to-container.d.ts +14 -3
  59. package/bin/utils/add-module-to-container.js +330 -111
  60. package/bin/utils/cli-ui.d.ts +20 -1
  61. package/bin/utils/cli-ui.js +41 -3
  62. package/bin/utils/update-tsconfig-paths.js +73 -33
  63. package/package.json +22 -13
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.containerizeProject = void 0;
7
+ const process_1 = require("process");
7
8
  const chalk_1 = __importDefault(require("chalk"));
8
9
  const cli_ui_1 = require("../utils/cli-ui");
9
10
  const project_analyzer_1 = require("./analyzers/project-analyzer");
@@ -14,26 +15,23 @@ const ci_generator_1 = require("./generators/ci-generator");
14
15
  const bootstrap_analyzer_1 = require("./analyzers/bootstrap-analyzer");
15
16
  const containerizeProject = async (options) => {
16
17
  try {
17
- console.log(chalk_1.default.bold.cyan("\n🐳 ExpressoTS Containerization\n"));
18
+ (0, cli_ui_1.printSection)("🐳 ExpressoTS Containerization");
18
19
  // Step 1: Analyze project (if enabled)
19
20
  let analysis;
20
21
  if (options.analyze) {
21
- console.log(chalk_1.default.yellow("📊 Analyzing your project...\n"));
22
+ (0, cli_ui_1.printSection)("📊 Project Analysis");
22
23
  analysis = await (0, project_analyzer_1.analyzeProject)();
23
- console.log(chalk_1.default.white("Project Analysis:"));
24
- console.log(chalk_1.default.gray(` Node version: ${analysis.nodeVersion}`));
25
- console.log(chalk_1.default.gray(` Package manager: ${analysis.packageManager}`));
26
- console.log(chalk_1.default.gray(` Dependencies: ${analysis.dependencies.length}`));
27
- console.log(chalk_1.default.gray(` Controllers: ${analysis.controllers.length}`));
28
- // Warn about local dependencies
24
+ (0, cli_ui_1.printKeyValue)("Node version", analysis.nodeVersion);
25
+ (0, cli_ui_1.printKeyValue)("Package manager", analysis.packageManager);
26
+ (0, cli_ui_1.printKeyValue)("Dependencies", String(analysis.dependencies.length));
27
+ (0, cli_ui_1.printKeyValue)("Controllers", String(analysis.controllers.length));
29
28
  if (analysis.hasLocalDependencies) {
30
- console.log(chalk_1.default.yellow(`\n⚠️ Warning: Detected ${analysis.localDependencyPaths.length} local file dependencies`));
31
- console.log(chalk_1.default.gray(" These will be copied into the Docker image. For production,"));
32
- console.log(chalk_1.default.gray(" consider publishing to npm registry instead."));
29
+ process_1.stdout.write("\n");
30
+ (0, cli_ui_1.printWarning)(`Detected ${analysis.localDependencyPaths.length} local file dependencies`, "containerize");
31
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("These will be copied into the Docker image. For production,"));
32
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("consider publishing to npm registry instead."));
33
33
  }
34
- // Bootstrap configuration analysis
35
34
  printBootstrapAnalysis(analysis);
36
- console.log("");
37
35
  }
38
36
  // Step 2: Generate based on target
39
37
  switch (options.target) {
@@ -66,27 +64,29 @@ const containerizeProject = async (options) => {
66
64
  await (0, ci_generator_1.generateCIConfig)(options, analysis);
67
65
  }
68
66
  // Step 4: Success message
69
- console.log(chalk_1.default.bold.green("\n✅ Container configuration generated successfully!\n"));
70
- console.log(chalk_1.default.white("📋 Summary:"));
71
- console.log(chalk_1.default.gray(" Generated files are fully customizable"));
72
- console.log(chalk_1.default.gray(" Edit them to fit your specific needs"));
73
- console.log(chalk_1.default.gray(" Run 'expressots container profile' to optimize"));
74
- console.log(chalk_1.default.cyan("\n📖 Next steps:"));
67
+ process_1.stdout.write("\n");
68
+ (0, cli_ui_1.printSuccess)("Container configuration generated successfully", "containerize");
69
+ (0, cli_ui_1.printSection)("📋 Summary");
70
+ (0, cli_ui_1.printBullet)("Generated files are fully customizable");
71
+ (0, cli_ui_1.printBullet)("Edit them to fit your specific needs");
72
+ (0, cli_ui_1.printBullet)(`Run ${chalk_1.default.cyan("expressots container profile")} to optimize`);
73
+ (0, cli_ui_1.printSection)("📖 Next steps");
75
74
  if (analysis?.hasLocalDependencies) {
76
- console.log(chalk_1.default.white(" 1. Review generated files"));
77
- console.log(chalk_1.default.white(" 2. Run setup: npm run docker:setup"));
78
- console.log(chalk_1.default.white(" 3. Build: docker build -t myapp ."));
79
- console.log(chalk_1.default.white(" 4. Run: docker-compose up"));
80
- console.log(chalk_1.default.yellow("\n💡 Tip: The docker-setup.sh script copies local dependencies"));
81
- console.log(chalk_1.default.yellow(" to .docker-deps/ for the Docker build context."));
75
+ const pm = analysis.packageManager;
76
+ (0, cli_ui_1.printBullet)(`Review generated files`);
77
+ (0, cli_ui_1.printBullet)(`Run setup: ${chalk_1.default.cyan(`${pm} run docker:setup`)}`);
78
+ (0, cli_ui_1.printBullet)(`Build: ${chalk_1.default.cyan("docker build -t myapp .")}`);
79
+ (0, cli_ui_1.printBullet)(`Run: ${chalk_1.default.cyan("docker compose up")}`);
80
+ process_1.stdout.write("\n");
81
+ (0, cli_ui_1.printWarning)("docker-setup.js copies local dependencies to .docker-deps/ for the Docker build context.", "containerize");
82
82
  }
83
83
  else {
84
- console.log(chalk_1.default.white(" 1. Review generated files"));
85
- console.log(chalk_1.default.white(" 2. Customize as needed"));
86
- console.log(chalk_1.default.white(" 3. Build: docker build -t myapp ."));
87
- console.log(chalk_1.default.white(" 4. Run: docker-compose up"));
84
+ (0, cli_ui_1.printBullet)("Review generated files");
85
+ (0, cli_ui_1.printBullet)("Customize as needed");
86
+ (0, cli_ui_1.printBullet)(`Build: ${chalk_1.default.cyan("docker build -t myapp .")}`);
87
+ (0, cli_ui_1.printBullet)(`Run: ${chalk_1.default.cyan("docker compose up")}`);
88
88
  }
89
- console.log("");
89
+ process_1.stdout.write("\n");
90
90
  }
91
91
  catch (error) {
92
92
  (0, cli_ui_1.printError)(`Containerization failed: ${error instanceof Error ? error.message : String(error)}`, "containerize");
@@ -102,53 +102,51 @@ function printBootstrapAnalysis(analysis) {
102
102
  if (!bootstrapConfig.hasEnvFileConfig) {
103
103
  return; // No env file config, nothing to warn about
104
104
  }
105
- console.log(chalk_1.default.cyan("\n📋 Bootstrap Configuration:"));
105
+ (0, cli_ui_1.printSection)("📋 Bootstrap Configuration");
106
106
  // Show detected env file config
107
107
  if (bootstrapConfig.skipFileLoading || bootstrapConfig.ciMode) {
108
- console.log(chalk_1.default.green("Container-ready configuration detected"));
109
- console.log(chalk_1.default.gray(` Using ${bootstrapConfig.skipFileLoading ? "skipFileLoading" : "ciMode"} mode`));
108
+ (0, cli_ui_1.printSuccess)("Container-ready configuration detected", "bootstrap");
109
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray(`Using ${bootstrapConfig.skipFileLoading ? "skipFileLoading" : "ciMode"} mode`));
110
110
  return;
111
111
  }
112
112
  // Check if env files are needed
113
113
  const copyEnvFiles = (0, bootstrap_analyzer_1.shouldCopyEnvFiles)(bootstrapConfig);
114
114
  if (copyEnvFiles) {
115
- console.log(chalk_1.default.yellow(" ⚠️ Environment file configuration detected"));
116
- // Show existing env files
115
+ (0, cli_ui_1.printWarning)("Environment file configuration detected", "bootstrap");
117
116
  if (bootstrapConfig.existingEnvFiles.length > 0) {
118
- console.log(chalk_1.default.gray(" Existing env files:"));
117
+ (0, cli_ui_1.printBullet)(chalk_1.default.bold("Existing env files:"));
119
118
  bootstrapConfig.existingEnvFiles.forEach((file) => {
120
- console.log(chalk_1.default.green(` ✓ ${file}`));
119
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(` ✓ ${file}`));
121
120
  });
122
121
  }
123
- // Show missing env files
124
122
  if (bootstrapConfig.missingEnvFiles.length > 0) {
125
- console.log(chalk_1.default.gray(" Missing env files:"));
123
+ (0, cli_ui_1.printBullet)(chalk_1.default.bold("Missing env files:"));
126
124
  bootstrapConfig.missingEnvFiles.forEach((file) => {
127
- console.log(chalk_1.default.red(` ✗ ${file}`));
125
+ (0, cli_ui_1.printBullet)(chalk_1.default.red(` ✗ ${file}`));
128
126
  });
129
127
  }
130
- // Show required variables
131
128
  if (bootstrapConfig.requiredVariables.length > 0) {
132
- console.log(chalk_1.default.gray(" Required variables:"));
129
+ (0, cli_ui_1.printBullet)(chalk_1.default.bold("Required variables:"));
133
130
  bootstrapConfig.requiredVariables.forEach((varName) => {
134
- console.log(chalk_1.default.yellow(` • ${varName}`));
131
+ (0, cli_ui_1.printBullet)(chalk_1.default.yellow(` • ${varName}`));
135
132
  });
136
133
  }
137
134
  }
138
135
  // Show recommendations
139
136
  if (bootstrapConfig.recommendations.length > 0) {
140
- console.log(chalk_1.default.cyan("\n💡 Recommendations:"));
137
+ (0, cli_ui_1.printSection)("💡 Recommendations");
141
138
  bootstrapConfig.recommendations.forEach((rec) => {
142
- console.log(chalk_1.default.gray(` • ${rec}`));
139
+ (0, cli_ui_1.printBullet)(rec);
143
140
  });
144
141
  }
145
142
  // Special warning for missing required env files
146
143
  if (!bootstrapConfig.isContainerReady) {
147
- console.log(chalk_1.default.red("\n⚠️ Container may fail to start!"));
148
- console.log(chalk_1.default.gray(" The bootstrap configuration requires env files that are missing."));
149
- console.log(chalk_1.default.gray(" Options:"));
150
- console.log(chalk_1.default.gray(" 1. Create the missing env files before building"));
151
- console.log(chalk_1.default.gray(" 2. Update bootstrap to use skipFileLoading: true"));
152
- console.log(chalk_1.default.gray(" 3. Set environment variables in docker-compose.yml"));
144
+ process_1.stdout.write("\n");
145
+ (0, cli_ui_1.printError)("Container may fail to start!", "bootstrap");
146
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("The bootstrap configuration requires env files that are missing."));
147
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("Options:"));
148
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray(" 1. Create the missing env files before building"));
149
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray(" 2. Update bootstrap to use skipFileLoading: true"));
150
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray(" 3. Set environment variables in docker-compose.yml"));
153
151
  }
154
152
  }
@@ -7,12 +7,14 @@ exports.generateCIConfig = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
+ const process_1 = require("process");
10
11
  const package_manager_commands_1 = require("../../utils/package-manager-commands");
12
+ const cli_ui_1 = require("../../utils/cli-ui");
11
13
  async function generateCIConfig(options, analysis) {
12
14
  const cwd = process.cwd();
13
15
  const platform = options.ciPlatform || "github";
14
16
  const strategy = options.ciStrategy || "comprehensive";
15
- console.log(chalk_1.default.yellow(`📝 Generating CI/CD configuration for ${platform}...`));
17
+ (0, cli_ui_1.printSection)(`📝 Generating CI/CD configuration for ${platform}`);
16
18
  if (platform === "all") {
17
19
  await generateAllPlatforms(cwd, options, analysis);
18
20
  }
@@ -33,7 +35,7 @@ async function generateAllPlatforms(cwd, options, analysis) {
33
35
  for (const platform of platforms) {
34
36
  await generatePlatformConfig(cwd, platform, options, analysis);
35
37
  }
36
- console.log(chalk_1.default.green(` ✓ Generated CI/CD configs for all platforms`));
38
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Generated CI/CD configs for all platforms`));
37
39
  }
38
40
  async function generatePlatformConfig(cwd, platform, options, analysis) {
39
41
  if (platform !== "github") {
@@ -68,10 +70,12 @@ async function generatePlatformConfig(cwd, platform, options, analysis) {
68
70
  * already produced for the same platform.
69
71
  */
70
72
  function printSinglePipelinePlatformWarning(platform) {
71
- console.log(chalk_1.default.yellow(`\n⚠️ ${platform} only supports a single canonical pipeline config file.`));
72
- console.log(chalk_1.default.gray(" The CD-focused output below will overwrite anything previously"));
73
- console.log(chalk_1.default.gray(" generated by `expressots cicd init`. For non-GitHub platforms"));
74
- console.log(chalk_1.default.gray(" prefer `cicd init` (which already includes Docker build/push/deploy).\n"));
73
+ process_1.stdout.write("\n");
74
+ (0, cli_ui_1.printWarning)(`${platform} only supports a single canonical pipeline config file.`, "cicd");
75
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("The CD-focused output below will overwrite anything previously"));
76
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("generated by `expressots cicd init`. For non-GitHub platforms"));
77
+ (0, cli_ui_1.printBullet)(chalk_1.default.gray("prefer `cicd init` (which already includes Docker build/push/deploy)."));
78
+ process_1.stdout.write("\n");
75
79
  }
76
80
  // ==================== GITHUB ACTIONS ====================
77
81
  async function generateGitHubActions(cwd, options, analysis) {
@@ -83,7 +87,7 @@ async function generateGitHubActions(cwd, options, analysis) {
83
87
  // File name `cd-docker.yml` makes the CD intent explicit and
84
88
  // avoids visual clash with `cicd init`'s `ci.yml`.
85
89
  fs_1.default.writeFileSync(path_1.default.join(githubDir, "cd-docker.yml"), workflow, "utf-8");
86
- console.log(chalk_1.default.green(` ✓ Created .github/workflows/cd-docker.yml`));
90
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created .github/workflows/cd-docker.yml`));
87
91
  }
88
92
  function generateGitHubActionsWorkflow(options, analysis) {
89
93
  const strategy = options.ciStrategy || "comprehensive";
@@ -332,7 +336,7 @@ jobs:
332
336
  async function generateGitLabCI(cwd, options, analysis) {
333
337
  const workflow = generateGitLabCIConfig(options, analysis);
334
338
  fs_1.default.writeFileSync(path_1.default.join(cwd, ".gitlab-ci.yml"), workflow, "utf-8");
335
- console.log(chalk_1.default.green(` ✓ Created .gitlab-ci.yml`));
339
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created .gitlab-ci.yml`));
336
340
  }
337
341
  function generateGitLabCIConfig(options, analysis) {
338
342
  const nodeVersion = analysis?.nodeVersion || "20";
@@ -449,7 +453,7 @@ async function generateCircleCI(cwd, options, analysis) {
449
453
  }
450
454
  const config = generateCircleCIConfig(options, analysis);
451
455
  fs_1.default.writeFileSync(path_1.default.join(circleCIDir, "config.yml"), config, "utf-8");
452
- console.log(chalk_1.default.green(` ✓ Created .circleci/config.yml`));
456
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created .circleci/config.yml`));
453
457
  }
454
458
  function generateCircleCIConfig(options, analysis) {
455
459
  const nodeVersion = analysis?.nodeVersion || "20";
@@ -548,7 +552,7 @@ workflows:
548
552
  async function generateJenkinsfile(cwd, options, analysis) {
549
553
  const config = generateJenkinsfileConfig(options, analysis);
550
554
  fs_1.default.writeFileSync(path_1.default.join(cwd, "Jenkinsfile"), config, "utf-8");
551
- console.log(chalk_1.default.green(` ✓ Created Jenkinsfile`));
555
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created Jenkinsfile`));
552
556
  }
553
557
  function generateJenkinsfileConfig(options, analysis) {
554
558
  const nodeVersion = analysis?.nodeVersion || "20";
@@ -665,7 +669,7 @@ pipeline {
665
669
  async function generateBitbucketPipelines(cwd, options, analysis) {
666
670
  const config = generateBitbucketPipelinesConfig(options, analysis);
667
671
  fs_1.default.writeFileSync(path_1.default.join(cwd, "bitbucket-pipelines.yml"), config, "utf-8");
668
- console.log(chalk_1.default.green(` ✓ Created bitbucket-pipelines.yml`));
672
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created bitbucket-pipelines.yml`));
669
673
  }
670
674
  function generateBitbucketPipelinesConfig(options, analysis) {
671
675
  const nodeVersion = analysis?.nodeVersion || "20";
@@ -744,7 +748,7 @@ pipelines:
744
748
  async function generateAzureDevOps(cwd, options, analysis) {
745
749
  const config = generateAzureDevOpsConfig(options, analysis);
746
750
  fs_1.default.writeFileSync(path_1.default.join(cwd, "azure-pipelines.yml"), config, "utf-8");
747
- console.log(chalk_1.default.green(` ✓ Created azure-pipelines.yml`));
751
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created azure-pipelines.yml`));
748
752
  }
749
753
  function generateAzureDevOpsConfig(options, analysis) {
750
754
  const nodeVersion = analysis?.nodeVersion || "20";
@@ -9,9 +9,10 @@ const path_1 = __importDefault(require("path"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const template_loader_1 = require("./template-loader");
11
11
  const bootstrap_analyzer_1 = require("../analyzers/bootstrap-analyzer");
12
+ const cli_ui_1 = require("../../utils/cli-ui");
12
13
  async function generateDockerCompose(options, analysis) {
13
14
  const cwd = process.cwd();
14
- console.log(chalk_1.default.yellow(`📝 Generating docker-compose.yml...`));
15
+ (0, cli_ui_1.printSection)(`📝 Generating docker-compose.yml`);
15
16
  // Always generate production docker-compose.yml plus environment-specific if needed
16
17
  const environments = options.environment === "all"
17
18
  ? ["development", "production"]
@@ -39,7 +40,7 @@ async function generateDockerCompose(options, analysis) {
39
40
  : `docker-compose.${env}.yml`;
40
41
  const filepath = path_1.default.join(cwd, filename);
41
42
  fs_1.default.writeFileSync(filepath, result.content, "utf-8");
42
- console.log(chalk_1.default.green(` ✓ Created ${filename}`));
43
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created ${filename}`));
43
44
  }
44
45
  }
45
46
  exports.generateDockerCompose = generateDockerCompose;
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateDockerfiles = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ const process_1 = require("process");
9
10
  const chalk_1 = __importDefault(require("chalk"));
10
11
  const preset_registry_1 = require("../presets/preset-registry");
11
12
  const template_loader_1 = require("./template-loader");
13
+ const cli_ui_1 = require("../../utils/cli-ui");
12
14
  const bootstrap_analyzer_1 = require("../analyzers/bootstrap-analyzer");
13
15
  /**
14
16
  * Detects the entry point path by reading tsconfig.build.json
@@ -66,7 +68,7 @@ async function generateDockerfiles(options, analysis) {
66
68
  const cwd = process.cwd();
67
69
  const preset = (0, preset_registry_1.getPresetConfig)(options.preset);
68
70
  const entryPoint = detectEntryPoint(cwd);
69
- console.log(chalk_1.default.yellow(`📝 Generating Dockerfile${options.environment !== "all" ? `.${options.environment}` : "s"}...`));
71
+ (0, cli_ui_1.printSection)(`📝 Generating Dockerfile${options.environment !== "all" ? `.${options.environment}` : "s"}`);
70
72
  // Always generate production Dockerfile (as "Dockerfile")
71
73
  // Plus environment-specific if requested
72
74
  const environments = options.environment === "all"
@@ -88,26 +90,25 @@ async function generateDockerfiles(options, analysis) {
88
90
  const filename = env === "production" ? "Dockerfile" : `Dockerfile.${env}`;
89
91
  const filepath = path_1.default.join(cwd, filename);
90
92
  fs_1.default.writeFileSync(filepath, result.content, "utf-8");
91
- console.log(chalk_1.default.green(` ✓ Created ${filename}`));
93
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created ${filename}`));
92
94
  }
93
95
  // Generate .dockerignore
94
96
  const dockerignore = generateDockerignoreContent(analysis);
95
97
  fs_1.default.writeFileSync(path_1.default.join(cwd, ".dockerignore"), dockerignore, "utf-8");
96
- console.log(chalk_1.default.green(` ✓ Created .dockerignore`));
98
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created .dockerignore`));
97
99
  // Generate helper script for local dependencies ONLY if needed
98
100
  // This is a temporary solution for unpublished packages
99
101
  if (analysis?.hasLocalDependencies) {
100
102
  const setupScriptNode = generateDockerSetupScriptNode(analysis.localDependencyPaths);
101
103
  fs_1.default.writeFileSync(path_1.default.join(cwd, "docker-setup.js"), setupScriptNode, "utf-8");
102
- console.log(chalk_1.default.green(` ✓ Created docker-setup.js (for local dependencies)`));
104
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created docker-setup.js (for local dependencies)`));
103
105
  // Also update package.json with docker:setup script using the
104
106
  // detected package manager so the generated `docker:build`
105
107
  // composite script works for pnpm/yarn/bun users too.
106
108
  updatePackageJsonWithDockerScript(cwd, analysis?.packageManager ?? "npm");
107
- console.log(chalk_1.default.green(` ✓ Updated package.json with docker:setup script`));
108
- console.log(chalk_1.default.yellow(`\n⚠️ Note: .docker-deps/ and package.docker.json are temporary solutions`));
109
- console.log(chalk_1.default.yellow(` for local file dependencies. Once packages are published to npm,`));
110
- console.log(chalk_1.default.yellow(` you can remove these and use a simpler Dockerfile.\n`));
109
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Updated package.json with docker:setup script`));
110
+ process_1.stdout.write("\n");
111
+ (0, cli_ui_1.printWarning)(".docker-deps/ and package.docker.json are temporary solutions for local file dependencies. Once packages are published to npm, you can remove these and use a simpler Dockerfile.", "containerize");
111
112
  }
112
113
  }
113
114
  exports.generateDockerfiles = generateDockerfiles;
@@ -216,11 +217,17 @@ function generateProductionDockerfile(nodeVersion, packageManager, port, preset,
216
217
  const localDepCopies = hasLocalDeps
217
218
  ? generateLocalDependencyCopies(analysis.localDependencyPaths, packageManager)
218
219
  : "";
219
- // Package file handling - only use package.docker.json for local deps
220
+ // Package file handling - only use package.docker.json for local deps.
221
+ // When local deps are present we deliberately do NOT copy the host
222
+ // lockfile: it still references the original `file:` paths from the
223
+ // host (e.g. `file:../expressots/...tgz`) which won't resolve inside
224
+ // the container. npm install will recreate the lockfile from the
225
+ // rewritten package.docker.json.
220
226
  const packageCopySection = hasLocalDeps
221
227
  ? `# Copy package files (use Docker-modified version for local dependencies)
222
- COPY package.docker.json ./package.json
223
- COPY package-lock.json* ./`
228
+ # Lockfile is intentionally omitted: the host lockfile references file:../
229
+ # paths that don't exist inside the container.
230
+ COPY package.docker.json ./package.json`
224
231
  : `# Copy package files
225
232
  COPY package*.json ./
226
233
  COPY package-lock.json* ./`;
@@ -249,7 +256,7 @@ ${packageManager === "pnpm" ? "COPY pnpm-lock.yaml ./" : ""}
249
256
  ${packageManager === "yarn" ? "COPY yarn.lock ./" : ""}
250
257
 
251
258
  # Install ALL dependencies (including devDependencies for build)
252
- ${getInstallCommand(packageManager, false)}
259
+ ${hasLocalDeps ? getLocalDepsInstallCommand(packageManager) : getInstallCommand(packageManager, false)}
253
260
 
254
261
  # Copy source code
255
262
  COPY . .
@@ -345,7 +352,7 @@ ${localDepCopies}
345
352
  ${packageCopySection}
346
353
 
347
354
  # Install dependencies
348
- ${getInstallCommand(packageManager, true)}
355
+ ${hasLocalDeps ? getLocalDepsInstallCommand(packageManager) : getInstallCommand(packageManager, true)}
349
356
 
350
357
  # Copy source code
351
358
  COPY . .
@@ -591,25 +598,40 @@ try {
591
598
 
592
599
  console.log(' Creating Docker-compatible package.json...');
593
600
 
594
- // Update file: paths to use .docker-deps
601
+ // Read the user's package.json
595
602
  const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
596
603
 
597
- if (pkg.dependencies) {
598
- Object.keys(pkg.dependencies).forEach(key => {
599
- if (pkg.dependencies[key].startsWith('file:')) {
600
- const filename = pkg.dependencies[key].split('/').pop();
601
- pkg.dependencies[key] = 'file:.docker-deps/' + filename;
602
- }
604
+ // Track all rewritten file: deps so we can force npm to use the
605
+ // flattened .docker-deps/ paths for any TRANSITIVE references too.
606
+ // This is critical: published tarballs of local packages bake in
607
+ // their own \`file:../...\` paths (e.g. core's package.json depends
608
+ // on shared via \`file:../shared/...tgz\`). Without overrides, npm
609
+ // would try to resolve those original host paths from inside
610
+ // node_modules and fail with ENOENT in the container.
611
+ const overrides = {};
612
+
613
+ const rewriteFileDeps = (depGroup) => {
614
+ if (!depGroup) return;
615
+ Object.keys(depGroup).forEach((key) => {
616
+ const value = depGroup[key];
617
+ if (typeof value !== 'string' || !value.startsWith('file:')) return;
618
+ const filename = value.split('/').pop();
619
+ // Only rewrite tarball references; directory file: refs are
620
+ // intentionally left as-is and will surface as a build failure
621
+ // the user can address (publish or replace with a tarball).
622
+ if (!filename.endsWith('.tgz')) return;
623
+ const newPath = 'file:.docker-deps/' + filename;
624
+ depGroup[key] = newPath;
625
+ overrides[key] = newPath;
603
626
  });
604
- }
627
+ };
605
628
 
606
- if (pkg.devDependencies) {
607
- Object.keys(pkg.devDependencies).forEach(key => {
608
- if (pkg.devDependencies[key].startsWith('file:')) {
609
- const filename = pkg.devDependencies[key].split('/').pop();
610
- pkg.devDependencies[key] = 'file:.docker-deps/' + filename;
611
- }
612
- });
629
+ rewriteFileDeps(pkg.dependencies);
630
+ rewriteFileDeps(pkg.devDependencies);
631
+
632
+ if (Object.keys(overrides).length > 0) {
633
+ pkg.overrides = Object.assign({}, pkg.overrides || {}, overrides);
634
+ console.log(' Added overrides for ' + Object.keys(overrides).length + ' transitive file: deps');
613
635
  }
614
636
 
615
637
  fs.writeFileSync('package.docker.json', JSON.stringify(pkg, null, 2) + '\\n', 'utf-8');
@@ -8,13 +8,14 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const template_loader_1 = require("./template-loader");
11
+ const cli_ui_1 = require("../../utils/cli-ui");
11
12
  async function generateKubernetesConfigs(options, analysis) {
12
13
  const cwd = process.cwd();
13
14
  const k8sDir = path_1.default.join(cwd, "k8s");
14
15
  if (!fs_1.default.existsSync(k8sDir)) {
15
16
  fs_1.default.mkdirSync(k8sDir, { recursive: true });
16
17
  }
17
- console.log(chalk_1.default.yellow(`📝 Generating Kubernetes configurations...`));
18
+ (0, cli_ui_1.printSection)(`📝 Generating Kubernetes configurations`);
18
19
  const vars = {
19
20
  port: analysis?.port || 3000,
20
21
  memory: analysis?.estimatedMemory || "256Mi",
@@ -28,17 +29,17 @@ async function generateKubernetesConfigs(options, analysis) {
28
29
  const deploymentResult = await (0, template_loader_1.loadKubernetesTemplate)("deployment", vars, () => generateDeployment(options, analysis));
29
30
  (0, template_loader_1.logTemplateSource)("k8s/deployment", deploymentResult.source);
30
31
  fs_1.default.writeFileSync(path_1.default.join(k8sDir, "deployment.yaml"), deploymentResult.content, "utf-8");
31
- console.log(chalk_1.default.green(` ✓ Created k8s/deployment.yaml`));
32
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created k8s/deployment.yaml`));
32
33
  // Generate service
33
34
  const serviceResult = await (0, template_loader_1.loadKubernetesTemplate)("service", vars, () => generateService(options, analysis));
34
35
  (0, template_loader_1.logTemplateSource)("k8s/service", serviceResult.source);
35
36
  fs_1.default.writeFileSync(path_1.default.join(k8sDir, "service.yaml"), serviceResult.content, "utf-8");
36
- console.log(chalk_1.default.green(` ✓ Created k8s/service.yaml`));
37
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created k8s/service.yaml`));
37
38
  // Generate config map
38
39
  const configMapResult = await (0, template_loader_1.loadKubernetesTemplate)("configmap", vars, () => generateConfigMap(options, analysis));
39
40
  (0, template_loader_1.logTemplateSource)("k8s/configmap", configMapResult.source);
40
41
  fs_1.default.writeFileSync(path_1.default.join(k8sDir, "configmap.yaml"), configMapResult.content, "utf-8");
41
- console.log(chalk_1.default.green(` ✓ Created k8s/configmap.yaml`));
42
+ (0, cli_ui_1.printBullet)(chalk_1.default.green(`✓ Created k8s/configmap.yaml`));
42
43
  }
43
44
  exports.generateKubernetesConfigs = generateKubernetesConfigs;
44
45
  function generateDeployment(options, analysis) {
@@ -1,5 +1,5 @@
1
1
  import { CommandModule } from "yargs";
2
- type CommandModuleArgs = {};
2
+ type CommandModuleArgs = Record<string, never>;
3
3
  export type CloudProvider = "aws" | "gcp" | "azure" | "railway" | "render" | "fly" | "digitalocean" | "heroku";
4
4
  declare const costsCommand: () => CommandModule<CommandModuleArgs, any>;
5
5
  export { costsCommand };
package/bin/costs/cli.js CHANGED
@@ -7,6 +7,7 @@ exports.costsCommand = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const form_1 = require("./form");
9
9
  const pricing_manager_1 = require("./pricing-manager");
10
+ const cli_ui_1 = require("../utils/cli-ui");
10
11
  const costsCommand = () => {
11
12
  return {
12
13
  command: "costs <action>",
@@ -143,7 +144,8 @@ const costsCommand = () => {
143
144
  await showPricingInfo();
144
145
  break;
145
146
  default:
146
- console.log(`Unknown action: ${action}`);
147
+ (0, cli_ui_1.printError)(`Unknown action: ${action}`, "costs");
148
+ process.exit(1);
147
149
  }
148
150
  },
149
151
  };
@@ -153,7 +155,7 @@ exports.costsCommand = costsCommand;
153
155
  * Update pricing data from remote sources
154
156
  */
155
157
  async function updatePricingData() {
156
- console.log(chalk_1.default.cyan("\n🔄 Updating Pricing Data...\n"));
158
+ (0, cli_ui_1.printSection)("🔄 Updating Pricing Data...");
157
159
  const manager = (0, pricing_manager_1.getPricingManager)();
158
160
  manager.clearCache();
159
161
  const success = await manager.updateCache();
package/bin/dev/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { CommandModule } from "yargs";
2
- type CommandModuleArgs = {};
2
+ type CommandModuleArgs = Record<string, never>;
3
3
  declare const devContainerCommand: () => CommandModule<CommandModuleArgs, any>;
4
4
  export { devContainerCommand };
package/bin/dev/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.devContainerCommand = void 0;
4
4
  const form_1 = require("./form");
5
+ const cli_ui_1 = require("../utils/cli-ui");
5
6
  const devContainerCommand = () => {
6
7
  return {
7
8
  command: "container-dev [action]",
@@ -126,7 +127,8 @@ const devContainerCommand = () => {
126
127
  await (0, form_1.showLogs)(options);
127
128
  break;
128
129
  default:
129
- console.log(`Unknown action: ${action}`);
130
+ (0, cli_ui_1.printError)(`Unknown action: ${action}`, "container-dev");
131
+ process.exit(1);
130
132
  }
131
133
  },
132
134
  };
@@ -1,4 +1,4 @@
1
1
  import { CommandModule } from "yargs";
2
- type CommandModuleArgs = {};
2
+ type CommandModuleArgs = Record<string, never>;
3
3
  declare const generateProject: () => CommandModule<CommandModuleArgs, any>;
4
4
  export { generateProject };
@@ -1,12 +1,12 @@
1
- import { defineConfig, Env, loadEnvSync } from "@expressots/core";
2
-
3
- loadEnvSync({ files: { development: ".env.local", production: ".env.prod" } });
4
-
5
- export const {{moduleName}}Config = defineConfig({
6
- enabled: Env.boolean("{{envPrefix}}_ENABLED", { default: true }),
7
- // Add more config options as needed
8
- });
9
-
10
- export const config = {{moduleName}}Config.values;
11
- export type {{className}}Config = typeof config;
12
-
1
+ import { defineConfig, Env, loadEnvSync } from "@expressots/core";
2
+
3
+ loadEnvSync({ files: { development: ".env.local", production: ".env.prod" } });
4
+
5
+ export const {{moduleName}}Config = defineConfig({
6
+ enabled: Env.boolean("{{envPrefix}}_ENABLED", { default: true }),
7
+ // Add more config options as needed
8
+ });
9
+
10
+ export const config = {{moduleName}}Config.values;
11
+ export type {{className}}Config = typeof config;
12
+
@@ -1,10 +1,10 @@
1
- /**
2
- * {{className}} Event
3
- */
4
- export class {{className}}Event {
5
- constructor(
6
- public readonly data: Record<string, unknown>,
7
- public readonly timestamp: Date = new Date(),
8
- ) {}
9
- }
10
-
1
+ /**
2
+ * {{className}} Event
3
+ */
4
+ export class {{className}}Event {
5
+ constructor(
6
+ public readonly data: Record<string, unknown>,
7
+ public readonly timestamp: Date = new Date(),
8
+ ) {}
9
+ }
10
+
@@ -1,18 +1,18 @@
1
- import { provide } from "@expressots/core";
2
- import { Request, Response, NextFunction } from "express";
3
-
4
- @provide({{className}}Guard)
5
- export class {{className}}Guard {
6
- canActivate(req: Request, res: Response, next: NextFunction): void {
7
- const authHeader = req.headers.authorization;
8
-
9
- if (!authHeader) {
10
- res.status(401).json({ message: "Unauthorized" });
11
- return;
12
- }
13
-
14
- // TODO: Validate token
15
- next();
16
- }
17
- }
18
-
1
+ import { provide } from "@expressots/core";
2
+ import { Request, Response, NextFunction } from "express";
3
+
4
+ @provide({{className}}Guard)
5
+ export class {{className}}Guard {
6
+ canActivate(req: Request, res: Response, next: NextFunction): void {
7
+ const authHeader = req.headers.authorization;
8
+
9
+ if (!authHeader) {
10
+ res.status(401).json({ message: "Unauthorized" });
11
+ return;
12
+ }
13
+
14
+ // TODO: Validate token
15
+ next();
16
+ }
17
+ }
18
+