codecrypto-cli 1.0.9 → 1.0.10

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 (137) hide show
  1. package/dist/commands/deploy.d.ts.map +1 -1
  2. package/dist/commands/deploy.js +296 -97
  3. package/dist/commands/deploy.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/commands/deploy.ts +327 -107
  6. package/docker-build/Dockerfile +0 -18
  7. package/docker-build/standalone/.next/BUILD_ID +0 -1
  8. package/docker-build/standalone/.next/app-path-routes-manifest.json +0 -6
  9. package/docker-build/standalone/.next/build-manifest.json +0 -19
  10. package/docker-build/standalone/.next/package.json +0 -1
  11. package/docker-build/standalone/.next/prerender-manifest.json +0 -114
  12. package/docker-build/standalone/.next/required-server-files.json +0 -164
  13. package/docker-build/standalone/.next/routes-manifest.json +0 -68
  14. package/docker-build/standalone/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
  15. package/docker-build/standalone/.next/server/app/_global-error/page/build-manifest.json +0 -16
  16. package/docker-build/standalone/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
  17. package/docker-build/standalone/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
  18. package/docker-build/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
  19. package/docker-build/standalone/.next/server/app/_global-error/page.js +0 -11
  20. package/docker-build/standalone/.next/server/app/_global-error/page.js.map +0 -5
  21. package/docker-build/standalone/.next/server/app/_global-error/page.js.nft.json +0 -1
  22. package/docker-build/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +0 -2
  23. package/docker-build/standalone/.next/server/app/_global-error.html +0 -2
  24. package/docker-build/standalone/.next/server/app/_global-error.meta +0 -15
  25. package/docker-build/standalone/.next/server/app/_global-error.rsc +0 -13
  26. package/docker-build/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  27. package/docker-build/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +0 -13
  28. package/docker-build/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +0 -6
  29. package/docker-build/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +0 -4
  30. package/docker-build/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  31. package/docker-build/standalone/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
  32. package/docker-build/standalone/.next/server/app/_not-found/page/build-manifest.json +0 -16
  33. package/docker-build/standalone/.next/server/app/_not-found/page/next-font-manifest.json +0 -11
  34. package/docker-build/standalone/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
  35. package/docker-build/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
  36. package/docker-build/standalone/.next/server/app/_not-found/page.js +0 -14
  37. package/docker-build/standalone/.next/server/app/_not-found/page.js.map +0 -5
  38. package/docker-build/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
  39. package/docker-build/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -2
  40. package/docker-build/standalone/.next/server/app/_not-found.html +0 -1
  41. package/docker-build/standalone/.next/server/app/_not-found.meta +0 -16
  42. package/docker-build/standalone/.next/server/app/_not-found.rsc +0 -14
  43. package/docker-build/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +0 -14
  44. package/docker-build/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  45. package/docker-build/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  46. package/docker-build/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  47. package/docker-build/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -4
  48. package/docker-build/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  49. package/docker-build/standalone/.next/server/app/favicon.ico/route/app-paths-manifest.json +0 -3
  50. package/docker-build/standalone/.next/server/app/favicon.ico/route/build-manifest.json +0 -11
  51. package/docker-build/standalone/.next/server/app/favicon.ico/route.js +0 -6
  52. package/docker-build/standalone/.next/server/app/favicon.ico/route.js.map +0 -5
  53. package/docker-build/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  54. package/docker-build/standalone/.next/server/app/favicon.ico.body +0 -0
  55. package/docker-build/standalone/.next/server/app/favicon.ico.meta +0 -1
  56. package/docker-build/standalone/.next/server/app/index.html +0 -1
  57. package/docker-build/standalone/.next/server/app/index.meta +0 -14
  58. package/docker-build/standalone/.next/server/app/index.rsc +0 -16
  59. package/docker-build/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -5
  60. package/docker-build/standalone/.next/server/app/index.segments/_full.segment.rsc +0 -16
  61. package/docker-build/standalone/.next/server/app/index.segments/_head.segment.rsc +0 -6
  62. package/docker-build/standalone/.next/server/app/index.segments/_index.segment.rsc +0 -5
  63. package/docker-build/standalone/.next/server/app/index.segments/_tree.segment.rsc +0 -4
  64. package/docker-build/standalone/.next/server/app/page/app-paths-manifest.json +0 -3
  65. package/docker-build/standalone/.next/server/app/page/build-manifest.json +0 -16
  66. package/docker-build/standalone/.next/server/app/page/next-font-manifest.json +0 -11
  67. package/docker-build/standalone/.next/server/app/page/react-loadable-manifest.json +0 -1
  68. package/docker-build/standalone/.next/server/app/page/server-reference-manifest.json +0 -4
  69. package/docker-build/standalone/.next/server/app/page.js +0 -16
  70. package/docker-build/standalone/.next/server/app/page.js.map +0 -5
  71. package/docker-build/standalone/.next/server/app/page.js.nft.json +0 -1
  72. package/docker-build/standalone/.next/server/app/page_client-reference-manifest.js +0 -2
  73. package/docker-build/standalone/.next/server/app-paths-manifest.json +0 -6
  74. package/docker-build/standalone/.next/server/chunks/66d90_example-navidad__next-internal_server_app_favicon_ico_route_actions_a30dceae.js +0 -3
  75. package/docker-build/standalone/.next/server/chunks/[externals]_next_dist_03fe02e0._.js +0 -3
  76. package/docker-build/standalone/.next/server/chunks/[root-of-the-server]__4b49000d._.js +0 -21
  77. package/docker-build/standalone/.next/server/chunks/[turbopack]_runtime.js +0 -795
  78. package/docker-build/standalone/.next/server/chunks/ssr/10072_infra_example-navidad__next-internal_server_app_page_actions_a387aef9.js +0 -3
  79. package/docker-build/standalone/.next/server/chunks/ssr/2025_cc_CODECRYPTO_infra_example-navidad_377109b0._.js +0 -3
  80. package/docker-build/standalone/.next/server/chunks/ssr/2025_cc_CODECRYPTO_infra_example-navidad_90799f30._.js +0 -4
  81. package/docker-build/standalone/.next/server/chunks/ssr/2025_cc_CODECRYPTO_infra_example-navidad_app_b9057478._.js +0 -3
  82. package/docker-build/standalone/.next/server/chunks/ssr/66d90_example-navidad__next-internal_server_app__global-error_page_actions_a079475f.js +0 -3
  83. package/docker-build/standalone/.next/server/chunks/ssr/66d90_example-navidad__next-internal_server_app__not-found_page_actions_ac88cec2.js +0 -3
  84. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_51be1544._.js +0 -4
  85. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_8dba0557._.js +0 -6
  86. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_client_components_41330064._.js +0 -3
  87. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_client_components_builtin_forbidden_ff3a0457.js +0 -3
  88. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_client_components_builtin_global-error_e0d57e6e.js +0 -3
  89. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_client_components_builtin_unauthorized_f1c47e13.js +0 -3
  90. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_d332f292._.js +0 -3
  91. package/docker-build/standalone/.next/server/chunks/ssr/77a20_next_dist_esm_build_templates_app-page_cf6afba1.js +0 -4
  92. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__02f3f427._.js +0 -3
  93. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__2f460610._.js +0 -4
  94. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__4722e53c._.js +0 -3
  95. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__53b749fa._.js +0 -3
  96. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__61942f2d._.js +0 -3
  97. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__7707ad1b._.js +0 -3
  98. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__864cadbf._.js +0 -10
  99. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__9939e281._.js +0 -3
  100. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__b4b0aa8a._.js +0 -3
  101. package/docker-build/standalone/.next/server/chunks/ssr/[root-of-the-server]__d2b7072b._.js +0 -3
  102. package/docker-build/standalone/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -795
  103. package/docker-build/standalone/.next/server/functions-config-manifest.json +0 -4
  104. package/docker-build/standalone/.next/server/middleware-build-manifest.js +0 -20
  105. package/docker-build/standalone/.next/server/middleware-manifest.json +0 -6
  106. package/docker-build/standalone/.next/server/next-font-manifest.js +0 -1
  107. package/docker-build/standalone/.next/server/next-font-manifest.json +0 -15
  108. package/docker-build/standalone/.next/server/pages/404.html +0 -1
  109. package/docker-build/standalone/.next/server/pages/500.html +0 -2
  110. package/docker-build/standalone/.next/server/pages-manifest.json +0 -4
  111. package/docker-build/standalone/.next/server/server-reference-manifest.js +0 -1
  112. package/docker-build/standalone/.next/server/server-reference-manifest.json +0 -5
  113. package/docker-build/standalone/.next/static/_NbfI2TKfapiyxsQgIG3h/_buildManifest.js +0 -11
  114. package/docker-build/standalone/.next/static/_NbfI2TKfapiyxsQgIG3h/_clientMiddlewareManifest.json +0 -1
  115. package/docker-build/standalone/.next/static/_NbfI2TKfapiyxsQgIG3h/_ssgManifest.js +0 -1
  116. package/docker-build/standalone/.next/static/chunks/57d1af92f5dc15fa.js +0 -1
  117. package/docker-build/standalone/.next/static/chunks/6ae71d5e8ea4d1eb.js +0 -1
  118. package/docker-build/standalone/.next/static/chunks/70977d70c9306213.js +0 -1
  119. package/docker-build/standalone/.next/static/chunks/777dac7104fe2a30.js +0 -5
  120. package/docker-build/standalone/.next/static/chunks/a6dad97d9634a72d.js +0 -1
  121. package/docker-build/standalone/.next/static/chunks/a6dad97d9634a72d.js.map +0 -1
  122. package/docker-build/standalone/.next/static/chunks/d6aec49b013224a2.css +0 -3
  123. package/docker-build/standalone/.next/static/chunks/turbopack-0ce517fb6224c1f0.js +0 -4
  124. package/docker-build/standalone/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  125. package/docker-build/standalone/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  126. package/docker-build/standalone/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  127. package/docker-build/standalone/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  128. package/docker-build/standalone/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  129. package/docker-build/standalone/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  130. package/docker-build/standalone/.next/static/media/favicon.0b3bf435.ico +0 -0
  131. package/docker-build/standalone/package.json +0 -26
  132. package/docker-build/standalone/public/file.svg +0 -1
  133. package/docker-build/standalone/public/globe.svg +0 -1
  134. package/docker-build/standalone/public/next.svg +0 -1
  135. package/docker-build/standalone/public/vercel.svg +0 -1
  136. package/docker-build/standalone/public/window.svg +0 -1
  137. package/docker-build/standalone/server.js +0 -38
@@ -7,8 +7,8 @@ import * as path from 'path';
7
7
  import * as os from 'os';
8
8
 
9
9
  export const deployCommand = new Command('deploy')
10
- .description('Build and deploy Docker image for Next.js standalone application')
11
- .argument('<project-path>', 'Path to the Next.js project directory')
10
+ .description('Build and deploy Docker image for Next.js or Express application')
11
+ .argument('<project-path>', 'Path to the project directory')
12
12
  .argument('[dest-folder]', 'Destination folder for Docker build context', './docker-build')
13
13
  .option('--skip-build', 'Skip npm run build (assume already built)', false)
14
14
  .option('--no-push', 'Build image but do not push to registry')
@@ -123,129 +123,164 @@ export const deployCommand = new Command('deploy')
123
123
  // - Con --no-push: options.push = false (no hace push)
124
124
  const shouldPush = options.push !== false;
125
125
 
126
+ // Detectar automáticamente el tipo de proyecto analizando package.json
127
+ const allDependencies = {
128
+ ...(packageJson.dependencies || {}),
129
+ ...(packageJson.devDependencies || {}),
130
+ };
131
+
132
+ let projectType: 'nextjs' | 'express' | null = null;
133
+
134
+ if (allDependencies.next || allDependencies['nextjs']) {
135
+ projectType = 'nextjs';
136
+ } else if (allDependencies.express) {
137
+ projectType = 'express';
138
+ }
139
+
140
+ if (!projectType) {
141
+ console.error(chalk.red(`❌ Error: No se pudo detectar el tipo de proyecto`));
142
+ console.error(chalk.yellow(' El proyecto debe tener "next" o "express" en las dependencias del package.json'));
143
+ console.error(chalk.yellow(' Dependencias encontradas:'));
144
+ const depKeys = Object.keys(allDependencies).slice(0, 10);
145
+ depKeys.forEach(dep => {
146
+ console.error(chalk.gray(` - ${dep}`));
147
+ });
148
+ if (Object.keys(allDependencies).length > 10) {
149
+ console.error(chalk.gray(` ... y ${Object.keys(allDependencies).length - 10} más`));
150
+ }
151
+ process.exit(1);
152
+ }
153
+
126
154
  console.log(chalk.gray('Deployment Configuration:'));
127
155
  console.log(chalk.white(` Project: ${chalk.green(projectName)}`));
156
+ console.log(chalk.white(` Type: ${chalk.green(projectType)}`));
128
157
  console.log(chalk.white(` Version: ${chalk.green(version)}`));
129
158
  console.log(chalk.white(` Image: ${chalk.green(imageName)}`));
130
159
  console.log(chalk.white(` Latest: ${chalk.green(imageLatest)}`));
131
160
  console.log(chalk.white(` Push to Registry: ${shouldPush ? chalk.green('Yes') : chalk.yellow('No')}\n`));
132
161
 
133
- // Paso 1: Build del proyecto si es necesario
134
- if (!options.skipBuild) {
135
- const buildSpinner = ora('Building Next.js application...').start();
136
- try {
137
- execSync('npm run build', {
138
- cwd: resolvedProjectPath,
139
- stdio: 'pipe'
140
- });
141
- buildSpinner.succeed('Build completed successfully');
142
- } catch (error) {
143
- buildSpinner.fail('Build failed');
144
- console.error(chalk.red('Error during build. Make sure you have "output: standalone" in next.config.ts'));
145
- process.exit(1);
146
- }
147
- }
148
-
149
- // Verificar que existe .next/standalone
150
- const standalonePath = path.join(resolvedProjectPath, '.next', 'standalone');
151
- if (!fs.existsSync(standalonePath)) {
152
- console.error(chalk.red(`❌ Error: No se encontró .next/standalone`));
153
- console.error(chalk.yellow('Asegúrate de tener "output: standalone" en next.config.ts y ejecutar "npm run build"'));
154
- process.exit(1);
155
- }
156
-
157
- // Paso 2: Preparar directorio destino
162
+ // Preparar directorio destino
158
163
  const resolvedDestFolder = path.resolve(destFolder);
159
- const standaloneDest = path.join(resolvedDestFolder, 'standalone');
160
-
161
- const copySpinner = ora('Copying standalone files...').start();
162
164
  if (fs.existsSync(resolvedDestFolder)) {
163
165
  fs.rmSync(resolvedDestFolder, { recursive: true, force: true });
164
166
  }
165
- fs.mkdirSync(standaloneDest, { recursive: true });
167
+ fs.mkdirSync(resolvedDestFolder, { recursive: true });
166
168
 
167
- // Copiar contenido de standalone
168
- const nextDir = findDirectory(standalonePath, '.next');
169
- if (nextDir) {
170
- const parentDir = path.dirname(nextDir);
171
- copyDirectory(parentDir, standaloneDest);
172
- } else {
173
- copyDirectory(standalonePath, standaloneDest);
174
- }
175
-
176
- // Copiar .next/static
177
- const staticPath = path.join(resolvedProjectPath, '.next', 'static');
178
- if (fs.existsSync(staticPath)) {
179
- const staticDest = path.join(standaloneDest, '.next', 'static');
180
- fs.mkdirSync(staticDest, { recursive: true });
181
- copyDirectory(staticPath, staticDest);
182
- }
183
-
184
- // Copiar public
185
- const publicPath = path.join(resolvedProjectPath, 'public');
186
- if (fs.existsSync(publicPath)) {
187
- const publicDest = path.join(standaloneDest, 'public');
188
- fs.mkdirSync(publicDest, { recursive: true });
189
- copyDirectory(publicPath, publicDest);
190
- }
191
-
192
- // Verificar server.js
193
- const serverJsPath = path.join(standaloneDest, 'server.js');
194
- if (!fs.existsSync(serverJsPath)) {
195
- copySpinner.fail('server.js not found in standalone');
196
- process.exit(1);
197
- }
198
-
199
- copySpinner.succeed('Files copied successfully');
200
-
201
- // Leer y procesar archivo .env si existe
202
- let nextPublicEnvVars: string[] = [];
203
169
  let runtimeEnvVars: string[] = [];
170
+ let nextPublicEnvVars: string[] = [];
171
+ let dockerfileContent: string;
172
+
173
+ // Determinar ruta del archivo .env (común para ambos tipos de proyecto)
204
174
  const envFilePath = options.envFile
205
175
  ? path.resolve(options.envFile)
206
176
  : path.join(resolvedProjectPath, '.env');
207
-
208
- if (fs.existsSync(envFilePath)) {
209
- const envContent = fs.readFileSync(envFilePath, 'utf-8');
210
- const envLines = envContent.split('\n');
177
+
178
+ if (projectType === 'nextjs') {
179
+ // ========== LÓGICA PARA NEXT.JS ==========
211
180
 
212
- for (const line of envLines) {
213
- const trimmed = line.trim();
214
- // Ignorar comentarios y líneas vacías
215
- if (!trimmed || trimmed.startsWith('#')) continue;
216
-
217
- const equalIndex = trimmed.indexOf('=');
218
- if (equalIndex === -1) continue;
219
-
220
- const key = trimmed.substring(0, equalIndex).trim();
221
- const value = trimmed.substring(equalIndex + 1).trim();
181
+ // Paso 1: Build del proyecto si es necesario
182
+ if (!options.skipBuild) {
183
+ const buildSpinner = ora('Building Next.js application...').start();
184
+ try {
185
+ execSync('npm run build', {
186
+ cwd: resolvedProjectPath,
187
+ stdio: 'pipe'
188
+ });
189
+ buildSpinner.succeed('Build completed successfully');
190
+ } catch (error) {
191
+ buildSpinner.fail('Build failed');
192
+ console.error(chalk.red('Error during build. Make sure you have "output: standalone" in next.config.ts'));
193
+ process.exit(1);
194
+ }
195
+ }
196
+
197
+ // Verificar que existe .next/standalone
198
+ const standalonePath = path.join(resolvedProjectPath, '.next', 'standalone');
199
+ if (!fs.existsSync(standalonePath)) {
200
+ console.error(chalk.red(`❌ Error: No se encontró .next/standalone`));
201
+ console.error(chalk.yellow('Asegúrate de tener "output: standalone" en next.config.ts y ejecutar "npm run build"'));
202
+ process.exit(1);
203
+ }
204
+
205
+ // Paso 2: Preparar directorio destino
206
+ const standaloneDest = path.join(resolvedDestFolder, 'standalone');
207
+
208
+ const copySpinner = ora('Copying standalone files...').start();
209
+ fs.mkdirSync(standaloneDest, { recursive: true });
210
+
211
+ // Copiar contenido de standalone
212
+ const nextDir = findDirectory(standalonePath, '.next');
213
+ if (nextDir) {
214
+ const parentDir = path.dirname(nextDir);
215
+ copyDirectory(parentDir, standaloneDest);
216
+ } else {
217
+ copyDirectory(standalonePath, standaloneDest);
218
+ }
219
+
220
+ // Copiar .next/static
221
+ const staticPath = path.join(resolvedProjectPath, '.next', 'static');
222
+ if (fs.existsSync(staticPath)) {
223
+ const staticDest = path.join(standaloneDest, '.next', 'static');
224
+ fs.mkdirSync(staticDest, { recursive: true });
225
+ copyDirectory(staticPath, staticDest);
226
+ }
227
+
228
+ // Copiar public
229
+ const publicPath = path.join(resolvedProjectPath, 'public');
230
+ if (fs.existsSync(publicPath)) {
231
+ const publicDest = path.join(standaloneDest, 'public');
232
+ fs.mkdirSync(publicDest, { recursive: true });
233
+ copyDirectory(publicPath, publicDest);
234
+ }
235
+
236
+ // Verificar server.js
237
+ const serverJsPath = path.join(standaloneDest, 'server.js');
238
+ if (!fs.existsSync(serverJsPath)) {
239
+ copySpinner.fail('server.js not found in standalone');
240
+ process.exit(1);
241
+ }
242
+
243
+ copySpinner.succeed('Files copied successfully');
244
+
245
+ // Leer y procesar archivo .env si existe
246
+ // (envFilePath ya está definido arriba)
247
+ if (fs.existsSync(envFilePath)) {
248
+ const envContent = fs.readFileSync(envFilePath, 'utf-8');
249
+ const envLines = envContent.split('\n');
222
250
 
223
- if (key.startsWith('NEXT_PUBLIC_')) {
224
- nextPublicEnvVars.push(`${key}=${value}`);
225
- } else {
226
- runtimeEnvVars.push(`${key}=${value}`);
251
+ for (const line of envLines) {
252
+ const trimmed = line.trim();
253
+ // Ignorar comentarios y líneas vacías
254
+ if (!trimmed || trimmed.startsWith('#')) continue;
255
+
256
+ const equalIndex = trimmed.indexOf('=');
257
+ if (equalIndex === -1) continue;
258
+
259
+ const key = trimmed.substring(0, equalIndex).trim();
260
+ const value = trimmed.substring(equalIndex + 1).trim();
261
+
262
+ if (key.startsWith('NEXT_PUBLIC_')) {
263
+ nextPublicEnvVars.push(`${key}=${value}`);
264
+ } else {
265
+ runtimeEnvVars.push(`${key}=${value}`);
266
+ }
227
267
  }
228
268
  }
229
- }
230
269
 
231
- // Paso 3: Crear Dockerfile con variables NEXT_PUBLIC_*
232
- const dockerfileSpinner = ora('Creating Dockerfile...').start();
233
- const dockerfilePath = path.join(resolvedDestFolder, 'Dockerfile');
234
-
235
- // Construir ARG y ENV para NEXT_PUBLIC_*
236
- const buildArgs = nextPublicEnvVars.map(env => {
237
- const [key, ...valueParts] = env.split('=');
238
- const value = valueParts.join('=');
239
- return `ARG ${key}=${value}`;
240
- }).join('\n');
241
-
242
- const buildEnvs = nextPublicEnvVars.map(env => {
243
- const [key, ...valueParts] = env.split('=');
244
- const value = valueParts.join('=');
245
- return `ENV ${key}=${value}`;
246
- }).join('\n');
247
-
248
- const dockerfileContent = `FROM node:20-alpine
270
+ // Construir ARG y ENV para NEXT_PUBLIC_*
271
+ const buildArgs = nextPublicEnvVars.map(env => {
272
+ const [key, ...valueParts] = env.split('=');
273
+ const value = valueParts.join('=');
274
+ return `ARG ${key}=${value}`;
275
+ }).join('\n');
276
+
277
+ const buildEnvs = nextPublicEnvVars.map(env => {
278
+ const [key, ...valueParts] = env.split('=');
279
+ const value = valueParts.join('=');
280
+ return `ENV ${key}=${value}`;
281
+ }).join('\n');
282
+
283
+ dockerfileContent = `FROM node:20-alpine
249
284
  WORKDIR /app
250
285
 
251
286
  ENV NODE_ENV=production
@@ -264,12 +299,197 @@ EXPOSE 3000
264
299
  # Arrancamos directamente con Node
265
300
  CMD ["node", "server.js"]
266
301
  `;
302
+
303
+ } else {
304
+ // ========== LÓGICA PARA EXPRESS ==========
305
+
306
+ // Paso 1: Instalar dependencias de producción si es necesario
307
+ if (!options.skipBuild) {
308
+ const installSpinner = ora('Installing production dependencies...').start();
309
+ try {
310
+ // Verificar si node_modules existe
311
+ const nodeModulesPath = path.join(resolvedProjectPath, 'node_modules');
312
+ if (!fs.existsSync(nodeModulesPath)) {
313
+ execSync('npm install --production', {
314
+ cwd: resolvedProjectPath,
315
+ stdio: 'pipe'
316
+ });
317
+ }
318
+ installSpinner.succeed('Dependencies ready');
319
+ } catch (error) {
320
+ installSpinner.fail('Failed to install dependencies');
321
+ console.error(chalk.red('Error installing dependencies'));
322
+ process.exit(1);
323
+ }
324
+ }
325
+
326
+ // Paso 2: Copiar archivos del proyecto (sin node_modules, Docker lo instalará)
327
+ const copySpinner = ora('Copying project files...').start();
328
+
329
+ // Copiar todos los archivos excepto los que están en .dockerignore
330
+ const ignorePatterns = [
331
+ 'node_modules',
332
+ '.git',
333
+ '.next',
334
+ 'dist',
335
+ 'build',
336
+ '.env',
337
+ '.env.local',
338
+ '.env.*.local',
339
+ 'npm-debug.log*',
340
+ 'yarn-debug.log*',
341
+ 'yarn-error.log*',
342
+ '.DS_Store',
343
+ 'coverage',
344
+ '.nyc_output',
345
+ ];
346
+
347
+ function shouldIgnore(filePath: string, basePath: string): boolean {
348
+ const relativePath = path.relative(basePath, filePath);
349
+ return ignorePatterns.some(pattern => {
350
+ if (relativePath.includes(pattern)) return true;
351
+ // Verificar si es un archivo que empieza con el patrón
352
+ const parts = relativePath.split(path.sep);
353
+ return parts.some(part => part === pattern || part.startsWith(pattern));
354
+ });
355
+ }
356
+
357
+ function copyProjectFiles(src: string, dest: string): void {
358
+ if (!fs.existsSync(dest)) {
359
+ fs.mkdirSync(dest, { recursive: true });
360
+ }
361
+
362
+ const entries = fs.readdirSync(src, { withFileTypes: true });
363
+ for (const entry of entries) {
364
+ const srcPath = path.join(src, entry.name);
365
+ const destPath = path.join(dest, entry.name);
366
+
367
+ // Ignorar archivos y directorios según los patrones
368
+ if (shouldIgnore(srcPath, resolvedProjectPath)) {
369
+ continue;
370
+ }
371
+
372
+ try {
373
+ const stats = fs.lstatSync(srcPath);
374
+
375
+ if (stats.isDirectory()) {
376
+ copyProjectFiles(srcPath, destPath);
377
+ } else if (stats.isFile()) {
378
+ fs.copyFileSync(srcPath, destPath);
379
+ }
380
+ } catch (error: any) {
381
+ // Ignorar errores al copiar archivos especiales
382
+ if (error.code === 'ENOTSUP' || error.code === 'EINVAL' || error.code === 'EOPNOTSUPP') {
383
+ continue;
384
+ }
385
+ throw error;
386
+ }
387
+ }
388
+ }
389
+
390
+ // Copiar archivos del proyecto
391
+ copyProjectFiles(resolvedProjectPath, resolvedDestFolder);
392
+
393
+ // Verificar que existe package.json en el destino
394
+ const destPackageJson = path.join(resolvedDestFolder, 'package.json');
395
+ if (!fs.existsSync(destPackageJson)) {
396
+ copySpinner.fail('package.json not found');
397
+ console.error(chalk.red('❌ Error: package.json is required for Express application'));
398
+ process.exit(1);
399
+ }
400
+
401
+ // Verificar que existe un archivo principal
402
+ const packageJson = JSON.parse(fs.readFileSync(destPackageJson, 'utf-8'));
403
+ const mainFile = packageJson.main || 'index.js';
404
+ const mainFilePath = path.join(resolvedDestFolder, mainFile);
405
+
406
+ if (!fs.existsSync(mainFilePath)) {
407
+ // Buscar otros archivos comunes
408
+ const commonFiles = ['index.js', 'server.js', 'app.js', 'main.js'];
409
+ let found = false;
410
+ for (const file of commonFiles) {
411
+ if (fs.existsSync(path.join(resolvedDestFolder, file))) {
412
+ found = true;
413
+ break;
414
+ }
415
+ }
416
+ if (!found) {
417
+ copySpinner.fail(`Main entry file not found: ${mainFile}`);
418
+ console.error(chalk.red(`❌ Error: Could not find main entry file (${mainFile}) for Express application`));
419
+ process.exit(1);
420
+ }
421
+ }
422
+
423
+ copySpinner.succeed('Files copied successfully');
424
+
425
+ // Leer y procesar archivo .env si existe
426
+ // (envFilePath ya está definido arriba)
427
+ if (fs.existsSync(envFilePath)) {
428
+ const envContent = fs.readFileSync(envFilePath, 'utf-8');
429
+ const envLines = envContent.split('\n');
430
+
431
+ for (const line of envLines) {
432
+ const trimmed = line.trim();
433
+ // Ignorar comentarios y líneas vacías
434
+ if (!trimmed || trimmed.startsWith('#')) continue;
435
+
436
+ const equalIndex = trimmed.indexOf('=');
437
+ if (equalIndex === -1) continue;
438
+
439
+ const key = trimmed.substring(0, equalIndex).trim();
440
+ const value = trimmed.substring(equalIndex + 1).trim();
441
+
442
+ runtimeEnvVars.push(`${key}=${value}`);
443
+ }
444
+ }
445
+
446
+ // Crear Dockerfile optimizado para Express
447
+ // (destPackageJson, packageJson y mainFile ya están declaradas arriba)
448
+ const startScript = packageJson.scripts?.start;
449
+
450
+ // Determinar el comando de inicio
451
+ let startCmd: string[];
452
+ if (startScript) {
453
+ // Si hay un script start, usarlo
454
+ startCmd = startScript.split(' ');
455
+ } else {
456
+ // Si no, usar node con el archivo principal
457
+ startCmd = ['node', mainFile];
458
+ }
459
+
460
+ dockerfileContent = `FROM node:20-alpine
461
+
462
+ WORKDIR /app
463
+
464
+ ENV NODE_ENV=production
465
+ ENV PORT=3000
466
+
467
+ # Copiar package files primero para aprovechar cache de Docker
468
+ COPY package*.json ./
469
+
470
+ # Instalar solo dependencias de producción
471
+ RUN npm ci --only=production && npm cache clean --force
472
+
473
+ # Copiar código fuente
474
+ COPY . .
475
+
476
+ # Exponemos el puerto
477
+ EXPOSE 3000
478
+
479
+ # Comando de inicio
480
+ CMD ${JSON.stringify(startCmd)}
481
+ `;
482
+ }
483
+
484
+ // Paso 3: Crear Dockerfile
485
+ const dockerfileSpinner = ora('Creating Dockerfile...').start();
486
+ const dockerfilePath = path.join(resolvedDestFolder, 'Dockerfile');
267
487
  fs.writeFileSync(dockerfilePath, dockerfileContent);
268
488
 
269
489
  dockerfileSpinner.succeed('Dockerfile created');
270
490
 
271
- // Mostrar contenido del Dockerfile si hay variables NEXT_PUBLIC_*
272
- if (nextPublicEnvVars.length > 0) {
491
+ // Mostrar contenido del Dockerfile
492
+ if (projectType === 'nextjs' && nextPublicEnvVars.length > 0) {
273
493
  console.log(chalk.gray('\n📄 Dockerfile content:'));
274
494
  console.log(chalk.gray('─'.repeat(60)));
275
495
  console.log(chalk.white(dockerfileContent));
@@ -280,7 +500,7 @@ CMD ["node", "server.js"]
280
500
  console.log(chalk.gray(` - ${chalk.cyan(key)}`));
281
501
  });
282
502
  } else {
283
- console.log(chalk.gray('\n📄 Dockerfile created (no NEXT_PUBLIC_* variables found)'));
503
+ console.log(chalk.gray(`\n📄 Dockerfile created for ${projectType} application`));
284
504
  }
285
505
 
286
506
  // Paso 4: Verificar/crear builder multi-plataforma
@@ -1,18 +0,0 @@
1
- FROM node:20-alpine
2
- WORKDIR /app
3
-
4
- ENV NODE_ENV=production
5
- ENV PORT=3000
6
-
7
- ARG NEXT_PUBLIC_API_URL=http://localhost:3000
8
-
9
- ENV NEXT_PUBLIC_API_URL=http://localhost:3000
10
-
11
- # Copiamos el contenido de standalone
12
- COPY standalone/ .
13
-
14
- # Exponemos el puerto para Traefik
15
- EXPOSE 3000
16
-
17
- # Arrancamos directamente con Node
18
- CMD ["node", "server.js"]
@@ -1 +0,0 @@
1
- _NbfI2TKfapiyxsQgIG3h
@@ -1,6 +0,0 @@
1
- {
2
- "/_global-error/page": "/_global-error",
3
- "/_not-found/page": "/_not-found",
4
- "/favicon.ico/route": "/favicon.ico",
5
- "/page": "/"
6
- }
@@ -1,19 +0,0 @@
1
- {
2
- "pages": {
3
- "/_app": []
4
- },
5
- "devFiles": [],
6
- "polyfillFiles": [
7
- "static/chunks/a6dad97d9634a72d.js"
8
- ],
9
- "lowPriorityFiles": [
10
- "static/_NbfI2TKfapiyxsQgIG3h/_ssgManifest.js",
11
- "static/_NbfI2TKfapiyxsQgIG3h/_buildManifest.js"
12
- ],
13
- "rootMainFiles": [
14
- "static/chunks/6ae71d5e8ea4d1eb.js",
15
- "static/chunks/70977d70c9306213.js",
16
- "static/chunks/777dac7104fe2a30.js",
17
- "static/chunks/turbopack-0ce517fb6224c1f0.js"
18
- ]
19
- }
@@ -1 +0,0 @@
1
- {"type": "commonjs"}
@@ -1,114 +0,0 @@
1
- {
2
- "version": 4,
3
- "routes": {
4
- "/_global-error": {
5
- "experimentalBypassFor": [
6
- {
7
- "type": "header",
8
- "key": "next-action"
9
- },
10
- {
11
- "type": "header",
12
- "key": "content-type",
13
- "value": "multipart/form-data;.*"
14
- }
15
- ],
16
- "initialRevalidateSeconds": false,
17
- "srcRoute": "/_global-error",
18
- "dataRoute": "/_global-error.rsc",
19
- "allowHeader": [
20
- "host",
21
- "x-matched-path",
22
- "x-prerender-revalidate",
23
- "x-prerender-revalidate-if-generated",
24
- "x-next-revalidated-tags",
25
- "x-next-revalidate-tag-token"
26
- ]
27
- },
28
- "/_not-found": {
29
- "initialStatus": 404,
30
- "experimentalBypassFor": [
31
- {
32
- "type": "header",
33
- "key": "next-action"
34
- },
35
- {
36
- "type": "header",
37
- "key": "content-type",
38
- "value": "multipart/form-data;.*"
39
- }
40
- ],
41
- "initialRevalidateSeconds": false,
42
- "srcRoute": "/_not-found",
43
- "dataRoute": "/_not-found.rsc",
44
- "allowHeader": [
45
- "host",
46
- "x-matched-path",
47
- "x-prerender-revalidate",
48
- "x-prerender-revalidate-if-generated",
49
- "x-next-revalidated-tags",
50
- "x-next-revalidate-tag-token"
51
- ]
52
- },
53
- "/favicon.ico": {
54
- "initialHeaders": {
55
- "cache-control": "public, max-age=0, must-revalidate",
56
- "content-type": "image/x-icon",
57
- "x-next-cache-tags": "_N_T_/layout,_N_T_/favicon.ico/layout,_N_T_/favicon.ico/route,_N_T_/favicon.ico"
58
- },
59
- "experimentalBypassFor": [
60
- {
61
- "type": "header",
62
- "key": "next-action"
63
- },
64
- {
65
- "type": "header",
66
- "key": "content-type",
67
- "value": "multipart/form-data;.*"
68
- }
69
- ],
70
- "initialRevalidateSeconds": false,
71
- "srcRoute": "/favicon.ico",
72
- "dataRoute": null,
73
- "allowHeader": [
74
- "host",
75
- "x-matched-path",
76
- "x-prerender-revalidate",
77
- "x-prerender-revalidate-if-generated",
78
- "x-next-revalidated-tags",
79
- "x-next-revalidate-tag-token"
80
- ]
81
- },
82
- "/": {
83
- "experimentalBypassFor": [
84
- {
85
- "type": "header",
86
- "key": "next-action"
87
- },
88
- {
89
- "type": "header",
90
- "key": "content-type",
91
- "value": "multipart/form-data;.*"
92
- }
93
- ],
94
- "initialRevalidateSeconds": false,
95
- "srcRoute": "/",
96
- "dataRoute": "/index.rsc",
97
- "allowHeader": [
98
- "host",
99
- "x-matched-path",
100
- "x-prerender-revalidate",
101
- "x-prerender-revalidate-if-generated",
102
- "x-next-revalidated-tags",
103
- "x-next-revalidate-tag-token"
104
- ]
105
- }
106
- },
107
- "dynamicRoutes": {},
108
- "notFoundRoutes": [],
109
- "preview": {
110
- "previewModeId": "97906e79384b81f9f7dfdaf2500bd376",
111
- "previewModeSigningKey": "c8a81f6f74dd50351ecd5dbef909560e00593cf675251719bb21e6883e641dd0",
112
- "previewModeEncryptionKey": "19b4f3ad1cee9c6cca1d9181bc9538fb585641fa19708384b8411c7dc5bd32bc"
113
- }
114
- }