nept-frameworks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,849 @@
1
+ // src/lib/dockerfile-generator.ts
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ // src/framework-defaults.ts
6
+ var supported = {
7
+ next: {
8
+ displayName: "Next.js",
9
+ language: "TypeScript",
10
+ stackType: "fullstack",
11
+ defaultPort: 3000,
12
+ defaultRuntime: "node",
13
+ defaultVersion: "24.18.0-trixie-slim",
14
+ status: "supported",
15
+ deployment: {
16
+ kind: "js-artifact",
17
+ selfContained: true,
18
+ copies: [
19
+ ["/app/public", "./public"],
20
+ ["/app/.next/standalone", "./"],
21
+ ["/app/.next/static", "./.next/static"]
22
+ ],
23
+ entrypoint: "server.js"
24
+ }
25
+ },
26
+ nuxt: {
27
+ displayName: "Nuxt",
28
+ language: "TypeScript",
29
+ stackType: "fullstack",
30
+ defaultPort: 3000,
31
+ defaultRuntime: "node",
32
+ defaultVersion: "24.18.0-trixie-slim",
33
+ status: "supported",
34
+ deployment: {
35
+ kind: "js-artifact",
36
+ selfContained: true,
37
+ copies: [["/app/.output", "./.output"]],
38
+ entrypoint: ".output/server/index.mjs"
39
+ }
40
+ },
41
+ astro: {
42
+ displayName: "Astro",
43
+ language: "TypeScript",
44
+ stackType: "fullstack",
45
+ defaultPort: 3000,
46
+ defaultRuntime: "node",
47
+ defaultVersion: "24.18.0-trixie-slim",
48
+ status: "supported",
49
+ deployment: {
50
+ kind: "js-artifact",
51
+ selfContained: false,
52
+ copies: [["/app/dist", "./dist"]],
53
+ entrypoint: "dist/server/entry.mjs"
54
+ }
55
+ },
56
+ sveltekit: {
57
+ displayName: "SvelteKit",
58
+ language: "TypeScript",
59
+ stackType: "fullstack",
60
+ defaultPort: 3000,
61
+ defaultRuntime: "node",
62
+ defaultVersion: "24.18.0-trixie-slim",
63
+ status: "supported",
64
+ deployment: {
65
+ kind: "js-artifact",
66
+ selfContained: false,
67
+ copies: [["/app/build", "./build"]],
68
+ entrypoint: "build/index.js"
69
+ }
70
+ },
71
+ remix: {
72
+ displayName: "Remix",
73
+ language: "TypeScript",
74
+ stackType: "fullstack",
75
+ defaultPort: 3000,
76
+ defaultRuntime: "node",
77
+ defaultVersion: "24.18.0-trixie-slim",
78
+ status: "supported",
79
+ deployment: {
80
+ kind: "js-artifact",
81
+ selfContained: false,
82
+ copies: [["/app/build", "./build"]],
83
+ entrypoint: "node_modules/@remix-run/serve/dist/cli.js",
84
+ entrypointArgument: "build/server/index.js"
85
+ }
86
+ },
87
+ qwik: {
88
+ displayName: "Qwik",
89
+ language: "TypeScript",
90
+ stackType: "fullstack",
91
+ defaultPort: 3000,
92
+ defaultRuntime: "node",
93
+ defaultVersion: "24.18.0-trixie-slim",
94
+ status: "supported",
95
+ deployment: {
96
+ kind: "js-artifact",
97
+ selfContained: false,
98
+ copies: [
99
+ ["/app/dist", "./dist"],
100
+ ["/app/server", "./server"]
101
+ ],
102
+ entrypoint: "server/entry.node-server.js"
103
+ }
104
+ }
105
+ };
106
+ var planned = (displayName, language, stackType, defaultPort, defaultRuntime, defaultVersion) => ({
107
+ displayName,
108
+ language,
109
+ stackType,
110
+ defaultPort,
111
+ defaultRuntime,
112
+ defaultVersion,
113
+ status: "planned"
114
+ });
115
+ var excluded = (displayName, language, defaultPort) => ({
116
+ displayName,
117
+ language,
118
+ stackType: "static",
119
+ defaultPort,
120
+ defaultRuntime: "nginx",
121
+ defaultVersion: "1.29.8-alpine",
122
+ status: "excluded"
123
+ });
124
+ var FRAMEWORK_DEFAULTS = {
125
+ ...supported,
126
+ express: {
127
+ ...planned("Express", "JavaScript", "backend", 3000, "node", "24.18.0-trixie-slim"),
128
+ status: "supported",
129
+ deployment: { kind: "js-source", build: false, entrypoint: "server.js" }
130
+ },
131
+ nest: {
132
+ ...planned("NestJS", "TypeScript", "backend", 3000, "node", "24.18.0-trixie-slim"),
133
+ status: "supported",
134
+ deployment: { kind: "js-source", build: true, entrypoint: "dist/main.js" }
135
+ },
136
+ fastify: {
137
+ ...planned("Fastify", "JavaScript", "backend", 3000, "node", "24.18.0-trixie-slim"),
138
+ status: "supported",
139
+ deployment: { kind: "js-source", build: false, entrypoint: "server.js" }
140
+ },
141
+ koa: {
142
+ ...planned("Koa", "JavaScript", "backend", 3000, "node", "24.18.0-trixie-slim"),
143
+ status: "supported",
144
+ deployment: { kind: "js-source", build: false, entrypoint: "server.js" }
145
+ },
146
+ deno: {
147
+ ...planned("Deno", "TypeScript", "backend", 8000, "deno", "2.9.2"),
148
+ status: "supported",
149
+ deployment: { kind: "deno-source", entrypoint: "main.ts" }
150
+ },
151
+ bun: {
152
+ ...planned("Bun", "TypeScript", "backend", 3000, "bun", "1.3.14-slim"),
153
+ status: "supported",
154
+ deployment: { kind: "bun-source", entrypoint: "server.ts" }
155
+ },
156
+ django: {
157
+ ...planned("Django", "Python", "backend", 8000, "python", "3.14.6-slim-trixie"),
158
+ status: "supported",
159
+ deployment: { kind: "python", command: ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"] }
160
+ },
161
+ flask: {
162
+ ...planned("Flask", "Python", "backend", 8000, "python", "3.14.6-slim-trixie"),
163
+ status: "supported",
164
+ deployment: { kind: "python", command: ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"] }
165
+ },
166
+ fastapi: {
167
+ ...planned("FastAPI", "Python", "backend", 8000, "python", "3.14.6-slim-trixie"),
168
+ status: "supported",
169
+ deployment: { kind: "python", command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] }
170
+ },
171
+ python: {
172
+ ...planned("Python", "Python", "backend", 8000, "python", "3.14.6-slim-trixie"),
173
+ status: "supported",
174
+ deployment: { kind: "python", command: ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"] }
175
+ },
176
+ go: {
177
+ ...planned("Go", "Go", "backend", 8080, "go", "1.26.5-trixie"),
178
+ status: "supported",
179
+ deployment: { kind: "go" }
180
+ },
181
+ gin: {
182
+ ...planned("Gin", "Go", "backend", 8080, "go", "1.26.5-trixie"),
183
+ status: "supported",
184
+ deployment: { kind: "go" }
185
+ },
186
+ echo: {
187
+ ...planned("Echo", "Go", "backend", 1323, "go", "1.26.5-trixie"),
188
+ status: "supported",
189
+ deployment: { kind: "go" }
190
+ },
191
+ rust: {
192
+ ...planned("Rust", "Rust", "backend", 8080, "rust", "1.97.0-slim-trixie"),
193
+ status: "supported",
194
+ deployment: { kind: "rust" }
195
+ },
196
+ actix: {
197
+ ...planned("Actix Web", "Rust", "backend", 8080, "rust", "1.97.0-slim-trixie"),
198
+ status: "supported",
199
+ deployment: { kind: "rust" }
200
+ },
201
+ rocket: {
202
+ ...planned("Rocket", "Rust", "backend", 8000, "rust", "1.97.0-slim-trixie"),
203
+ status: "supported",
204
+ deployment: { kind: "rust" }
205
+ },
206
+ java: {
207
+ ...planned("Java", "Java", "backend", 8080, "java", "25.0.3_9-jre-noble"),
208
+ status: "supported",
209
+ deployment: { kind: "java", mode: "class" }
210
+ },
211
+ springBoot: {
212
+ ...planned("Spring Boot", "Java", "backend", 8080, "java", "25.0.3_9-jre-noble"),
213
+ status: "supported",
214
+ deployment: { kind: "java", mode: "jar" }
215
+ },
216
+ quarkus: {
217
+ ...planned("Quarkus", "Java", "backend", 8080, "java", "25.0.3_9-jre-noble"),
218
+ status: "supported",
219
+ deployment: { kind: "java", mode: "quarkus" }
220
+ },
221
+ ruby: {
222
+ ...planned("Ruby", "Ruby", "backend", 4567, "ruby", "4.0.5-slim-trixie"),
223
+ status: "supported",
224
+ deployment: { kind: "ruby", command: ["ruby", "app.rb"] }
225
+ },
226
+ rails: {
227
+ ...planned("Ruby on Rails", "Ruby", "backend", 3000, "ruby", "4.0.5-slim-trixie"),
228
+ status: "supported",
229
+ deployment: { kind: "ruby", command: ["bundle", "exec", "rails", "server", "-b", "0.0.0.0", "-p", "3000"] }
230
+ },
231
+ php: {
232
+ ...planned("PHP", "PHP", "backend", 8080, "php", "8.5.8-apache-trixie"),
233
+ status: "supported",
234
+ deployment: { kind: "php" }
235
+ },
236
+ laravel: {
237
+ ...planned("Laravel", "PHP", "backend", 8080, "php", "8.5.8-apache-trixie"),
238
+ status: "supported",
239
+ deployment: { kind: "php" }
240
+ },
241
+ symfony: {
242
+ ...planned("Symfony", "PHP", "backend", 8080, "php", "8.5.8-apache-trixie"),
243
+ status: "supported",
244
+ deployment: { kind: "php" }
245
+ },
246
+ csharp: {
247
+ ...planned("C#", "C#", "backend", 8080, "dotnet", "10.0-noble"),
248
+ status: "supported",
249
+ deployment: { kind: "dotnet", aspnet: false, dll: "App.dll" }
250
+ },
251
+ aspnet: {
252
+ ...planned("ASP.NET Core", "C#", "backend", 8080, "dotnet", "10.0-noble"),
253
+ status: "supported",
254
+ deployment: { kind: "dotnet", aspnet: true, dll: "App.dll" }
255
+ },
256
+ other: {
257
+ ...planned("Other", "Other", "backend", 8080, "custom", "required"),
258
+ status: "supported",
259
+ deployment: { kind: "custom" }
260
+ },
261
+ html: excluded("HTML/CSS/JS", "HTML", 80),
262
+ react: excluded("React", "JavaScript", 80),
263
+ vue: excluded("Vue", "JavaScript", 80),
264
+ angular: excluded("Angular", "TypeScript", 80),
265
+ svelte: excluded("Svelte", "JavaScript", 80),
266
+ vite: excluded("Vite", "JavaScript", 80),
267
+ gatsby: excluded("Gatsby", "JavaScript", 80),
268
+ solid: excluded("Solid", "JavaScript", 80)
269
+ };
270
+ function getJavaScriptDeployment(framework) {
271
+ const deployment = FRAMEWORK_DEFAULTS[framework].deployment;
272
+ if (deployment?.kind !== "js-artifact") {
273
+ throw new Error(`${framework} is not an artifact framework`);
274
+ }
275
+ return deployment;
276
+ }
277
+ function defaultVersionFor(runtime) {
278
+ if (runtime === "bun")
279
+ return "1.3.14-slim";
280
+ if (runtime === "deno")
281
+ return "2.9.2";
282
+ return "24.18.0-trixie-slim";
283
+ }
284
+
285
+ // src/lib/dockerfile-generator.ts
286
+ var packageManagers = {
287
+ bun: {
288
+ lockfile: "bun.lock",
289
+ install: "bun install --frozen-lockfile",
290
+ production: "bun install --frozen-lockfile --production",
291
+ build: "bun run build",
292
+ cache: "/root/.bun/install/cache"
293
+ },
294
+ npm: {
295
+ lockfile: "package-lock.json",
296
+ install: "npm ci --no-audit --no-fund",
297
+ production: "npm prune --omit=dev",
298
+ build: "npm run build",
299
+ cache: "/root/.npm"
300
+ },
301
+ pnpm: {
302
+ lockfile: "pnpm-lock.yaml",
303
+ install: "corepack enable && pnpm install --frozen-lockfile",
304
+ production: "pnpm prune --prod",
305
+ build: "pnpm run build",
306
+ cache: "/root/.local/share/pnpm/store"
307
+ },
308
+ yarn: {
309
+ lockfile: "yarn.lock",
310
+ install: "corepack enable && yarn install --immutable",
311
+ production: "yarn workspaces focus --production",
312
+ build: "yarn run build",
313
+ cache: "/root/.yarn/berry/cache"
314
+ }
315
+ };
316
+ var DOCKERIGNORE = `node_modules
317
+ .next
318
+ .nuxt
319
+ .output
320
+ dist
321
+ build
322
+ .svelte-kit
323
+ .astro
324
+ .git
325
+ .env*
326
+ !.env.example
327
+ coverage
328
+ *.log
329
+ `;
330
+ var NEXT_DOCKERIGNORE = DOCKERIGNORE;
331
+ function detectPackageManager(projectDir = ".") {
332
+ const matches = Object.entries(packageManagers).filter(([, value]) => existsSync(join(projectDir, value.lockfile)));
333
+ if (matches.length !== 1) {
334
+ throw new Error(matches.length === 0 ? `No supported lockfile found in ${projectDir}` : `Multiple lockfiles found in ${projectDir}: ${matches.map(([, value]) => value.lockfile).join(", ")}`);
335
+ }
336
+ return matches[0][0];
337
+ }
338
+ function inferPackageManager(installCommand) {
339
+ if (!installCommand)
340
+ return detectPackageManager();
341
+ const command = installCommand.trim();
342
+ if (/^(?:corepack enable && )?pnpm\b/.test(command))
343
+ return "pnpm";
344
+ if (/^(?:corepack enable && )?yarn\b/.test(command))
345
+ return "yarn";
346
+ if (/^npm\b/.test(command))
347
+ return "npm";
348
+ if (/^bun\b/.test(command))
349
+ return "bun";
350
+ return detectPackageManager();
351
+ }
352
+ function inferRuntime(framework, command) {
353
+ if (framework === "bun")
354
+ return "bun";
355
+ if (framework === "deno")
356
+ return "deno";
357
+ if (command && /^(?:\[\s*"bun"|bun\b)/.test(command.trim()))
358
+ return "bun";
359
+ return FRAMEWORK_DEFAULTS[framework].defaultRuntime;
360
+ }
361
+ function isJavaScriptFramework(framework) {
362
+ const kind = FRAMEWORK_DEFAULTS[framework].deployment?.kind;
363
+ return kind === "js-artifact" || kind === "js-source";
364
+ }
365
+ function applyParams(dockerfile, params, defaultOutput) {
366
+ let result = dockerfile;
367
+ if (params.outputDir && defaultOutput) {
368
+ if (/[\r\n]/.test(params.outputDir) || params.outputDir.includes("..")) {
369
+ throw new Error(`Invalid output directory: ${params.outputDir}`);
370
+ }
371
+ const output = `/app/${params.outputDir.replace(/^\.\//, "").replace(/\/$/, "")}`;
372
+ result = result.replaceAll(defaultOutput, output);
373
+ }
374
+ const buildInstructions = [
375
+ params.installCMD && `RUN ${singleLine(params.installCMD, "install command")}`,
376
+ ...(params.buildCMD ?? []).map((command) => `RUN ${singleLine(command, "build command")}`)
377
+ ].filter(Boolean).join(`
378
+ `);
379
+ if (buildInstructions)
380
+ result = insertBeforeRuntime(result, buildInstructions);
381
+ const environment = Object.entries(params.envVars).map(([key, value]) => {
382
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
383
+ throw new Error(`Invalid environment variable name: ${key}`);
384
+ }
385
+ return `ENV ${key}=${JSON.stringify(value)}`;
386
+ }).join(`
387
+ `);
388
+ if (environment)
389
+ result = insertBeforeFinalUser(result, environment);
390
+ if (params.runCMD) {
391
+ result = insertBeforeFinalCommand(result, `RUN ${singleLine(params.runCMD, "run command")}`);
392
+ }
393
+ if (params.cmd) {
394
+ result = result.replace(/CMD [^\n]+(?=\n?$)/, `CMD ${singleLine(params.cmd, "CMD")}`);
395
+ }
396
+ return result;
397
+ }
398
+ function insertBeforeRuntime(dockerfile, instructions) {
399
+ const firstStage = dockerfile.indexOf(`
400
+ FROM `);
401
+ const runtime = dockerfile.lastIndexOf(`
402
+ FROM `);
403
+ if (runtime !== -1 && runtime !== firstStage) {
404
+ return `${dockerfile.slice(0, runtime)}
405
+ ${instructions}
406
+ ${dockerfile.slice(runtime)}`;
407
+ }
408
+ return insertBeforeFinalUser(dockerfile, instructions);
409
+ }
410
+ function insertBeforeFinalUser(dockerfile, instructions) {
411
+ const user = dockerfile.lastIndexOf(`
412
+ USER `);
413
+ if (user !== -1) {
414
+ return `${dockerfile.slice(0, user)}
415
+ ${instructions}${dockerfile.slice(user)}`;
416
+ }
417
+ return insertBeforeFinalCommand(dockerfile, instructions);
418
+ }
419
+ function insertBeforeFinalCommand(dockerfile, instructions) {
420
+ const command = dockerfile.lastIndexOf(`
421
+ CMD `);
422
+ if (command === -1)
423
+ return `${dockerfile.trimEnd()}
424
+ ${instructions}
425
+ `;
426
+ return `${dockerfile.slice(0, command)}
427
+ ${instructions}${dockerfile.slice(command)}`;
428
+ }
429
+ function singleLine(value, label) {
430
+ if (!value.trim() || /[\r\n]/.test(value))
431
+ throw new Error(`Invalid ${label}`);
432
+ return value.trim();
433
+ }
434
+ function generateDockerfile(params) {
435
+ const framework = Object.keys(FRAMEWORK_DEFAULTS).find((candidate) => candidate.toLowerCase() === params.framework.toLowerCase());
436
+ if (!framework)
437
+ throw new Error(`Unsupported framework: ${params.framework}`);
438
+ const runtime = inferRuntime(framework, params.cmd);
439
+ const packageManager = inferPackageManager(params.installCMD);
440
+ const version = params.version || FRAMEWORK_DEFAULTS[framework].defaultVersion;
441
+ const bunVersion = runtime === "bun" || framework === "bun" ? version : defaultVersionFor("bun");
442
+ const composerVersion = "2.10.2";
443
+ const denoVersion = framework === "deno" ? version : defaultVersionFor("deno");
444
+ const dotnetVersion = framework === "csharp" || framework === "aspnet" ? version : FRAMEWORK_DEFAULTS.csharp.defaultVersion;
445
+ const goVersion = ["go", "gin", "echo"].includes(framework) ? version : FRAMEWORK_DEFAULTS.go.defaultVersion;
446
+ const javaVersion = ["java", "springBoot", "quarkus"].includes(framework) ? version : FRAMEWORK_DEFAULTS.java.defaultVersion;
447
+ const mavenVersion = "3.9.16-eclipse-temurin-25-noble";
448
+ const nodeVersion = isJavaScriptFramework(framework) && runtime === "node" ? version : defaultVersionFor("node");
449
+ const phpVersion = ["php", "laravel", "symfony"].includes(framework) ? version : FRAMEWORK_DEFAULTS.php.defaultVersion;
450
+ const pythonVersion = ["django", "flask", "fastapi", "python"].includes(framework) ? version : FRAMEWORK_DEFAULTS.python.defaultVersion;
451
+ const rubyVersion = ["ruby", "rails"].includes(framework) ? version : FRAMEWORK_DEFAULTS.ruby.defaultVersion;
452
+ const rustVersion = ["rust", "actix", "rocket"].includes(framework) ? version : FRAMEWORK_DEFAULTS.rust.defaultVersion;
453
+ const finish = (dockerfile, outputDir) => applyParams(dockerfile, params, outputDir);
454
+ validateImageTag(bunVersion);
455
+ validateImageTag(composerVersion);
456
+ validateImageTag(denoVersion);
457
+ validateImageTag(dotnetVersion);
458
+ validateImageTag(goVersion);
459
+ validateImageTag(javaVersion);
460
+ validateImageTag(mavenVersion);
461
+ validateImageTag(nodeVersion);
462
+ validateImageTag(phpVersion);
463
+ validateImageTag(pythonVersion);
464
+ validateImageTag(rubyVersion);
465
+ validateImageTag(rustVersion);
466
+ const deployment = FRAMEWORK_DEFAULTS[framework].deployment;
467
+ if (!deployment)
468
+ throw new Error(`Unsupported framework: ${framework}`);
469
+ if (deployment.kind === "deno-source") {
470
+ return finish(generateDenoDockerfile(deployment.entrypoint, denoVersion));
471
+ }
472
+ if (deployment.kind === "bun-source") {
473
+ return finish(generateBunSourceDockerfile(deployment.entrypoint, bunVersion));
474
+ }
475
+ if (deployment.kind === "python") {
476
+ return finish(generatePythonDockerfile(pythonVersion, FRAMEWORK_DEFAULTS[framework].defaultPort, deployment.command));
477
+ }
478
+ if (deployment.kind === "go") {
479
+ return finish(generateGoDockerfile(FRAMEWORK_DEFAULTS[framework].defaultPort, goVersion));
480
+ }
481
+ if (deployment.kind === "rust") {
482
+ return finish(generateRustDockerfile(FRAMEWORK_DEFAULTS[framework].defaultPort, rustVersion));
483
+ }
484
+ if (deployment.kind === "java") {
485
+ return finish(generateJavaDockerfile(deployment.mode, FRAMEWORK_DEFAULTS[framework].defaultPort, javaVersion, mavenVersion));
486
+ }
487
+ if (deployment.kind === "ruby") {
488
+ return finish(generateRubyDockerfile(rubyVersion, FRAMEWORK_DEFAULTS[framework].defaultPort, deployment.command));
489
+ }
490
+ if (deployment.kind === "php") {
491
+ return finish(generatePhpDockerfile(phpVersion, composerVersion));
492
+ }
493
+ if (deployment.kind === "dotnet") {
494
+ return finish(generateDotnetDockerfile(deployment.aspnet, deployment.dll, dotnetVersion));
495
+ }
496
+ if (deployment.kind === "custom") {
497
+ return generateParamDockerfile(params);
498
+ }
499
+ if (deployment.kind === "js-source") {
500
+ return finish(generateJavaScriptSourceDockerfile({
501
+ build: deployment.build,
502
+ bunVersion,
503
+ entrypoint: deployment.entrypoint,
504
+ nodeVersion,
505
+ packageManager,
506
+ runtime
507
+ }));
508
+ }
509
+ const manager = packageManager;
510
+ const commands = packageManagers[manager];
511
+ const target = getJavaScriptDeployment(framework);
512
+ if (!commands)
513
+ throw new Error(`Unsupported package manager: ${manager}`);
514
+ if (!target)
515
+ throw new Error(`Unsupported framework: ${framework}`);
516
+ if (runtime !== "bun" && runtime !== "node") {
517
+ throw new Error(`Unsupported runtime: ${runtime}`);
518
+ }
519
+ const buildImage = manager === "bun" ? `oven/bun:${bunVersion}` : `node:${nodeVersion}`;
520
+ const runtimeImage = runtime === "bun" ? `oven/bun:${bunVersion}` : `node:${nodeVersion}`;
521
+ const user = runtime === "bun" ? "bun" : "node";
522
+ const buildEnvironment = framework === "nuxt" ? `ENV NITRO_PRESET=${runtime === "bun" ? "bun" : "node-server"}
523
+ ` : "";
524
+ const prepareBuild = framework === "next" ? "mkdir -p public && " : "";
525
+ const buildCache = framework === "next" ? "--mount=type=cache,target=/app/.next/cache " : "";
526
+ const productionDependencies = target.selfContained ? "" : `
527
+ FROM dependencies AS production-dependencies
528
+ RUN ${commands.production}
529
+ `;
530
+ const prepareRuntime = framework === "next" ? `RUN mkdir .next && chown ${user}:${user} .next
531
+ ` : "";
532
+ const copyDependencies = target.selfContained ? "" : `COPY --from=production-dependencies --chown=${user}:${user} /app/node_modules ./node_modules
533
+ COPY --from=production-dependencies --chown=${user}:${user} /app/package.json ./package.json
534
+ `;
535
+ const copies = target.copies.map(([source, destination]) => `COPY --from=build --chown=${user}:${user} ${source} ${destination}`).join(`
536
+ `);
537
+ const command = [runtime, target.entrypoint, target.entrypointArgument].filter(Boolean).map((part) => JSON.stringify(part)).join(", ");
538
+ return finish(`# syntax=docker/dockerfile:1
539
+
540
+ ARG BUILD_IMAGE=${buildImage}
541
+ ARG RUNTIME_IMAGE=${runtimeImage}
542
+
543
+ FROM \${BUILD_IMAGE} AS dependencies
544
+ WORKDIR /app
545
+ COPY package.json ${commands.lockfile} ./
546
+ RUN --mount=type=cache,target=${commands.cache} ${commands.install}
547
+ ${productionDependencies}
548
+ FROM dependencies AS build
549
+ WORKDIR /app
550
+ COPY . .
551
+ ENV NEXT_TELEMETRY_DISABLED=1
552
+ ${buildEnvironment}RUN ${buildCache}${prepareBuild}${commands.build}
553
+
554
+ FROM \${RUNTIME_IMAGE} AS runtime
555
+ WORKDIR /app
556
+ ENV NODE_ENV=production \\
557
+ NEXT_TELEMETRY_DISABLED=1 \\
558
+ HOST=0.0.0.0 \\
559
+ HOSTNAME=0.0.0.0 \\
560
+ PORT=3000
561
+ ${prepareRuntime}${copyDependencies}${copies}
562
+ USER ${user}
563
+ EXPOSE 3000
564
+ CMD [${command}]
565
+ `, (target.copies.find(([, destination]) => destination === "./") ?? target.copies[0])?.[0]);
566
+ }
567
+ function generateJavaScriptSourceDockerfile({
568
+ build,
569
+ bunVersion,
570
+ entrypoint,
571
+ nodeVersion,
572
+ packageManager,
573
+ runtime
574
+ }) {
575
+ if (runtime !== "bun" && runtime !== "node") {
576
+ throw new Error(`Unsupported runtime for JavaScript server: ${runtime}`);
577
+ }
578
+ const commands = packageManagers[packageManager];
579
+ const buildImage = packageManager === "bun" ? `oven/bun:${bunVersion}` : `node:${nodeVersion}`;
580
+ const runtimeImage = runtime === "bun" ? `oven/bun:${bunVersion}` : `node:${nodeVersion}`;
581
+ const user = runtime;
582
+ const buildStage = build ? `
583
+ FROM dependencies AS build
584
+ COPY . .
585
+ RUN ${commands.build}
586
+ ` : "";
587
+ const sourceCopy = build ? `COPY --from=build --chown=${user}:${user} /app/dist ./dist
588
+ COPY --from=build --chown=${user}:${user} /app/public ./public` : `COPY --chown=${user}:${user} . .`;
589
+ return `# syntax=docker/dockerfile:1
590
+
591
+ FROM ${buildImage} AS dependencies
592
+ WORKDIR /app
593
+ COPY package.json ${commands.lockfile} ./
594
+ RUN --mount=type=cache,target=${commands.cache} ${commands.install}
595
+
596
+ FROM dependencies AS production-dependencies
597
+ RUN ${commands.production}
598
+ ${buildStage}
599
+ FROM ${runtimeImage} AS runtime
600
+ WORKDIR /app
601
+ ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
602
+ COPY --from=production-dependencies --chown=${user}:${user} /app/node_modules ./node_modules
603
+ COPY --from=production-dependencies --chown=${user}:${user} /app/package.json ./package.json
604
+ ${sourceCopy}
605
+ USER ${user}
606
+ EXPOSE 3000
607
+ CMD [${JSON.stringify(runtime)}, ${JSON.stringify(entrypoint)}]
608
+ `;
609
+ }
610
+ function generateDenoDockerfile(entrypoint, version) {
611
+ return `# syntax=docker/dockerfile:1
612
+
613
+ FROM denoland/deno:${version} AS dependencies
614
+ WORKDIR /app
615
+ COPY deno.json ./
616
+ COPY ${entrypoint} ./
617
+ RUN deno cache ${entrypoint}
618
+
619
+ FROM denoland/deno:${version} AS runtime
620
+ WORKDIR /app
621
+ ENV HOST=0.0.0.0 PORT=8000
622
+ COPY --from=dependencies --chown=deno:deno /deno-dir /deno-dir
623
+ COPY --chown=deno:deno . .
624
+ USER deno
625
+ EXPOSE 8000
626
+ CMD ["run", "--allow-net", "--allow-read", "--allow-env", "${entrypoint}"]
627
+ `;
628
+ }
629
+ function generateBunSourceDockerfile(entrypoint, bunVersion) {
630
+ return `# syntax=docker/dockerfile:1
631
+
632
+ FROM oven/bun:${bunVersion} AS dependencies
633
+ WORKDIR /app
634
+ COPY package.json bun.lock ./
635
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install --frozen-lockfile --production
636
+
637
+ FROM oven/bun:${bunVersion} AS runtime
638
+ WORKDIR /app
639
+ ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
640
+ COPY --from=dependencies --chown=bun:bun /app/node_modules ./node_modules
641
+ COPY --chown=bun:bun . .
642
+ USER bun
643
+ EXPOSE 3000
644
+ CMD ["bun", "${entrypoint}"]
645
+ `;
646
+ }
647
+ function generatePythonDockerfile(version, port, command) {
648
+ return `# syntax=docker/dockerfile:1
649
+
650
+ FROM python:${version} AS dependencies
651
+ WORKDIR /app
652
+ COPY requirements.txt ./
653
+ RUN --mount=type=cache,target=/root/.cache/pip pip install --prefix=/install -r requirements.txt
654
+
655
+ FROM python:${version} AS runtime
656
+ WORKDIR /app
657
+ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PORT=${port}
658
+ RUN useradd --create-home --uid 10001 app
659
+ COPY --from=dependencies /install /usr/local
660
+ COPY --chown=app:app . .
661
+ USER app
662
+ EXPOSE ${port}
663
+ CMD [${command.map((part) => JSON.stringify(part)).join(", ")}]
664
+ `;
665
+ }
666
+ function generateGoDockerfile(port, version) {
667
+ return `# syntax=docker/dockerfile:1
668
+
669
+ FROM golang:${version} AS build
670
+ WORKDIR /app
671
+ COPY go.mod go.sum* ./
672
+ RUN --mount=type=cache,target=/go/pkg/mod go mod download
673
+ COPY . .
674
+ RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/app .
675
+
676
+ FROM debian:13.5-slim AS runtime
677
+ WORKDIR /app
678
+ ENV PORT=${port}
679
+ COPY --from=build --chown=10001:10001 /out/app /app/app
680
+ USER 10001:10001
681
+ EXPOSE ${port}
682
+ CMD ["/app/app"]
683
+ `;
684
+ }
685
+ function generateRustDockerfile(port, version) {
686
+ return `# syntax=docker/dockerfile:1
687
+
688
+ FROM rust:${version} AS build
689
+ WORKDIR /app
690
+ COPY Cargo.toml Cargo.lock ./
691
+ COPY src ./src
692
+ RUN --mount=type=cache,target=/usr/local/cargo/registry --mount=type=cache,target=/app/target cargo build --locked --release && cp target/release/app /tmp/app
693
+
694
+ FROM debian:13.5-slim AS runtime
695
+ WORKDIR /app
696
+ ENV PORT=${port} ROCKET_ADDRESS=0.0.0.0 ROCKET_PORT=${port}
697
+ COPY --from=build --chown=10001:10001 /tmp/app /app/app
698
+ USER 10001:10001
699
+ EXPOSE ${port}
700
+ CMD ["/app/app"]
701
+ `;
702
+ }
703
+ function generateJavaDockerfile(mode, port, version, mavenVersion) {
704
+ const jdkVersion = version.replace("-jre-", "-jdk-");
705
+ if (mode === "class") {
706
+ return `# syntax=docker/dockerfile:1
707
+
708
+ FROM eclipse-temurin:${jdkVersion} AS build
709
+ WORKDIR /app
710
+ COPY Main.java ./
711
+ RUN javac Main.java
712
+
713
+ FROM eclipse-temurin:${version} AS runtime
714
+ WORKDIR /app
715
+ ENV PORT=${port}
716
+ COPY --from=build --chown=10001:10001 /app/Main.class ./
717
+ USER 10001:10001
718
+ EXPOSE ${port}
719
+ CMD ["java", "Main"]
720
+ `;
721
+ }
722
+ const copy = mode === "jar" ? "COPY --from=build --chown=10001:10001 /app/target/app.jar /app/app.jar" : "COPY --from=build --chown=10001:10001 /app/target/quarkus-app /app/quarkus-app";
723
+ const command = mode === "jar" ? '["java", "-jar", "/app/app.jar"]' : '["java", "-jar", "/app/quarkus-app/quarkus-run.jar"]';
724
+ return `# syntax=docker/dockerfile:1
725
+
726
+ FROM maven:${mavenVersion} AS build
727
+ WORKDIR /app
728
+ COPY pom.xml ./
729
+ RUN --mount=type=cache,target=/root/.m2 mvn --batch-mode dependency:go-offline
730
+ COPY src ./src
731
+ RUN --mount=type=cache,target=/root/.m2 mvn --batch-mode package -DskipTests
732
+
733
+ FROM eclipse-temurin:${version} AS runtime
734
+ WORKDIR /app
735
+ ENV PORT=${port}
736
+ ${copy}
737
+ USER 10001:10001
738
+ EXPOSE ${port}
739
+ CMD ${command}
740
+ `;
741
+ }
742
+ function generateRubyDockerfile(version, port, command) {
743
+ return `# syntax=docker/dockerfile:1
744
+
745
+ FROM ruby:${version} AS dependencies
746
+ WORKDIR /app
747
+ RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
748
+ COPY Gemfile ./
749
+ RUN --mount=type=cache,target=/usr/local/bundle/cache bundle config set without development:test && bundle lock && bundle install
750
+
751
+ FROM ruby:${version} AS runtime
752
+ WORKDIR /app
753
+ ENV RAILS_ENV=production RACK_ENV=production PORT=${port}
754
+ RUN useradd --create-home --uid 10001 app
755
+ COPY --from=dependencies /usr/local/bundle /usr/local/bundle
756
+ COPY --chown=app:app . .
757
+ COPY --from=dependencies --chown=app:app /app/Gemfile.lock ./Gemfile.lock
758
+ RUN mkdir -p tmp/pids log storage && chown -R app:app tmp log storage
759
+ USER app
760
+ EXPOSE ${port}
761
+ CMD [${command.map((part) => JSON.stringify(part)).join(", ")}]
762
+ `;
763
+ }
764
+ function generatePhpDockerfile(version, composerVersion) {
765
+ return `# syntax=docker/dockerfile:1
766
+
767
+ FROM composer:${composerVersion} AS dependencies
768
+ WORKDIR /app
769
+ COPY composer.json ./
770
+ RUN --mount=type=cache,target=/tmp/cache COMPOSER_CACHE_DIR=/tmp/cache composer install --no-dev --no-interaction --classmap-authoritative
771
+ COPY . .
772
+ RUN composer dump-autoload --no-dev --classmap-authoritative
773
+
774
+ FROM php:${version} AS runtime
775
+ WORKDIR /app
776
+ ENV APACHE_DOCUMENT_ROOT=/app/public
777
+ RUN a2enmod rewrite && sed -ri 's!Listen 80!Listen 8080!; s!AllowOverride None!AllowOverride All!g' /etc/apache2/ports.conf /etc/apache2/apache2.conf && rm -rf /var/www/html && ln -s /app/public /var/www/html && mkdir -p /var/run/apache2 /var/lock/apache2 /var/log/apache2 && chown -R www-data:www-data /var/run/apache2 /var/lock/apache2 /var/log/apache2 /app
778
+ COPY --from=dependencies --chown=www-data:www-data /app /app
779
+ RUN mkdir -p storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && chown -R www-data:www-data storage bootstrap/cache
780
+ USER www-data
781
+ EXPOSE 8080
782
+ CMD ["apache2-foreground"]
783
+ `;
784
+ }
785
+ function generateDotnetDockerfile(aspnet, dll, version) {
786
+ const runtime = aspnet ? "aspnet" : "runtime";
787
+ return `# syntax=docker/dockerfile:1
788
+
789
+ FROM mcr.microsoft.com/dotnet/sdk:${version} AS build
790
+ WORKDIR /src
791
+ COPY *.csproj ./
792
+ RUN --mount=type=cache,target=/root/.nuget/packages dotnet restore
793
+ COPY . .
794
+ RUN --mount=type=cache,target=/root/.nuget/packages dotnet publish -c Release -o /out --no-restore
795
+
796
+ FROM mcr.microsoft.com/dotnet/${runtime}:${version} AS runtime
797
+ WORKDIR /app
798
+ ENV ASPNETCORE_HTTP_PORTS=8080 PORT=8080
799
+ COPY --from=build --chown=app:app /out ./
800
+ USER app
801
+ EXPOSE 8080
802
+ CMD ["dotnet", "${dll}"]
803
+ `;
804
+ }
805
+ function generateParamDockerfile(params) {
806
+ const image = params.version.includes(":") ? params.version : `node:${params.version}`;
807
+ validateImage(image);
808
+ const workdir = params.outputDir ? `/app/${params.outputDir.replace(/^\.\//, "").replace(/\/$/, "")}` : "/app";
809
+ return applyParams(`# syntax=docker/dockerfile:1
810
+
811
+ FROM ${image}
812
+ WORKDIR /app
813
+ COPY --chown=node:node . .
814
+ WORKDIR ${workdir}
815
+ USER node
816
+ CMD ["node", "server.js"]
817
+ `, { ...params, outputDir: null });
818
+ }
819
+ function validateImage(value) {
820
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_./:-]*$/.test(value)) {
821
+ throw new Error(`Invalid container image: ${value}`);
822
+ }
823
+ }
824
+ function validateImageTag(value) {
825
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(value)) {
826
+ throw new Error(`Invalid container image tag: ${value}`);
827
+ }
828
+ }
829
+
830
+ // src/lib/next-js/next-dockerfile-generator.ts
831
+ function generateNextDockerfile(options) {
832
+ return generateDockerfile({ ...options, framework: "next" });
833
+ }
834
+
835
+ // src/index.ts
836
+ var detectPackageManager2 = detectPackageManager;
837
+ var DOCKERIGNORE2 = DOCKERIGNORE;
838
+ var FRAMEWORK_DEFAULTS2 = FRAMEWORK_DEFAULTS;
839
+ var generateDockerfile2 = generateDockerfile;
840
+ var generateNextDockerfile2 = generateNextDockerfile;
841
+ var NEXT_DOCKERIGNORE2 = NEXT_DOCKERIGNORE;
842
+ export {
843
+ generateNextDockerfile2 as generateNextDockerfile,
844
+ generateDockerfile2 as generateDockerfile,
845
+ detectPackageManager2 as detectPackageManager,
846
+ NEXT_DOCKERIGNORE2 as NEXT_DOCKERIGNORE,
847
+ FRAMEWORK_DEFAULTS2 as FRAMEWORK_DEFAULTS,
848
+ DOCKERIGNORE2 as DOCKERIGNORE
849
+ };