@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.
- package/bin/cicd/cli.d.ts +1 -1
- package/bin/cicd/cli.js +3 -1
- package/bin/cicd/form.js +5 -4
- package/bin/cli.d.ts +1 -5
- package/bin/cli.js +56 -6
- package/bin/commands/project.commands.js +233 -26
- package/bin/containerize/cli.d.ts +1 -1
- package/bin/containerize/cli.js +1 -1
- package/bin/containerize/form.js +49 -51
- package/bin/containerize/generators/ci-generator.js +16 -12
- package/bin/containerize/generators/docker-compose-generator.js +3 -2
- package/bin/containerize/generators/dockerfile-generator.js +50 -28
- package/bin/containerize/generators/kubernetes-generator.js +5 -4
- package/bin/costs/cli.d.ts +1 -1
- package/bin/costs/cli.js +4 -2
- package/bin/dev/cli.d.ts +1 -1
- package/bin/dev/cli.js +3 -1
- package/bin/generate/cli.d.ts +1 -1
- package/bin/generate/templates/nonopinionated/config.tpl +12 -12
- package/bin/generate/templates/nonopinionated/event.tpl +10 -10
- package/bin/generate/templates/nonopinionated/guard.tpl +18 -18
- package/bin/generate/templates/nonopinionated/handler.tpl +12 -12
- package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -27
- package/bin/generate/templates/opinionated/config.tpl +47 -47
- package/bin/generate/templates/opinionated/event.tpl +15 -15
- package/bin/generate/templates/opinionated/guard.tpl +41 -41
- package/bin/generate/templates/opinionated/handler.tpl +23 -23
- package/bin/generate/templates/opinionated/interceptor.tpl +50 -50
- package/bin/generate/utils/command-utils.d.ts +13 -2
- package/bin/generate/utils/command-utils.js +50 -17
- package/bin/generate/utils/opinionated-cmd.js +19 -12
- package/bin/help/cli.d.ts +1 -1
- package/bin/help/command-help-registry.d.ts +23 -0
- package/bin/help/command-help-registry.js +303 -0
- package/bin/help/command-help.d.ts +36 -0
- package/bin/help/command-help.js +56 -0
- package/bin/help/form.js +127 -30
- package/bin/help/main-help.d.ts +8 -0
- package/bin/help/main-help.js +126 -0
- package/bin/help/render.d.ts +32 -0
- package/bin/help/render.js +46 -0
- package/bin/info/cli.d.ts +1 -1
- package/bin/info/form.d.ts +1 -1
- package/bin/info/form.js +11 -11
- package/bin/migrate/cli.d.ts +1 -1
- package/bin/migrate/cli.js +3 -1
- package/bin/migrate/form.js +4 -3
- package/bin/new/cli.d.ts +5 -1
- package/bin/new/cli.js +62 -14
- package/bin/new/form.d.ts +3 -1
- package/bin/new/form.js +338 -23
- package/bin/profile/cli.d.ts +1 -1
- package/bin/profile/cli.js +3 -1
- package/bin/profile/form.js +5 -4
- package/bin/providers/create/form.js +53 -4
- package/bin/studio/cli.js +9 -3
- package/bin/templates/cli.js +7 -5
- package/bin/utils/add-module-to-container.d.ts +14 -3
- package/bin/utils/add-module-to-container.js +330 -111
- package/bin/utils/cli-ui.d.ts +20 -1
- package/bin/utils/cli-ui.js +41 -3
- package/bin/utils/update-tsconfig-paths.js +73 -33
- package/package.json +22 -13
package/bin/containerize/form.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
22
|
+
(0, cli_ui_1.printSection)("📊 Project Analysis");
|
|
22
23
|
analysis = await (0, project_analyzer_1.analyzeProject)();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
(0, cli_ui_1.printSection)("📋 Bootstrap Configuration");
|
|
106
106
|
// Show detected env file config
|
|
107
107
|
if (bootstrapConfig.skipFileLoading || bootstrapConfig.ciMode) {
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
// Show existing env files
|
|
115
|
+
(0, cli_ui_1.printWarning)("Environment file configuration detected", "bootstrap");
|
|
117
116
|
if (bootstrapConfig.existingEnvFiles.length > 0) {
|
|
118
|
-
|
|
117
|
+
(0, cli_ui_1.printBullet)(chalk_1.default.bold("Existing env files:"));
|
|
119
118
|
bootstrapConfig.existingEnvFiles.forEach((file) => {
|
|
120
|
-
|
|
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
|
-
|
|
123
|
+
(0, cli_ui_1.printBullet)(chalk_1.default.bold("Missing env files:"));
|
|
126
124
|
bootstrapConfig.missingEnvFiles.forEach((file) => {
|
|
127
|
-
|
|
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
|
-
|
|
129
|
+
(0, cli_ui_1.printBullet)(chalk_1.default.bold("Required variables:"));
|
|
133
130
|
bootstrapConfig.requiredVariables.forEach((varName) => {
|
|
134
|
-
|
|
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
|
-
|
|
137
|
+
(0, cli_ui_1.printSection)("💡 Recommendations");
|
|
141
138
|
bootstrapConfig.recommendations.forEach((rec) => {
|
|
142
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
//
|
|
601
|
+
// Read the user's package.json
|
|
595
602
|
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
|
596
603
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/bin/costs/cli.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
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
|
-
|
|
130
|
+
(0, cli_ui_1.printError)(`Unknown action: ${action}`, "container-dev");
|
|
131
|
+
process.exit(1);
|
|
130
132
|
}
|
|
131
133
|
},
|
|
132
134
|
};
|
package/bin/generate/cli.d.ts
CHANGED
|
@@ -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
|
+
|