@akash-chowdhury-24/deployhub 1.0.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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/install.ps1 +55 -0
  4. package/install.sh +99 -0
  5. package/package.json +86 -0
  6. package/src/adapters/dotnet.adapter.js +41 -0
  7. package/src/adapters/go.adapter.js +40 -0
  8. package/src/adapters/index.js +48 -0
  9. package/src/adapters/java.adapter.js +46 -0
  10. package/src/adapters/node.adapter.js +77 -0
  11. package/src/adapters/php.adapter.js +43 -0
  12. package/src/adapters/python.adapter.js +54 -0
  13. package/src/adapters/rails.adapter.js +66 -0
  14. package/src/artifact/engine.js +473 -0
  15. package/src/cli/index.js +45 -0
  16. package/src/commands/artifact.js +88 -0
  17. package/src/commands/build.js +44 -0
  18. package/src/commands/clean.js +50 -0
  19. package/src/commands/deploy.js +82 -0
  20. package/src/commands/doctor.js +630 -0
  21. package/src/commands/init.js +795 -0
  22. package/src/commands/logs.js +33 -0
  23. package/src/commands/rollback.js +50 -0
  24. package/src/commands/storage.js +116 -0
  25. package/src/commands/update.js +63 -0
  26. package/src/commands/verify.js +55 -0
  27. package/src/core/config.js +168 -0
  28. package/src/core/pipeline.js +77 -0
  29. package/src/core/stages.js +210 -0
  30. package/src/deployment/index.js +156 -0
  31. package/src/deployment/providers/azure-vm.js +7 -0
  32. package/src/deployment/providers/docker.js +30 -0
  33. package/src/deployment/providers/ec2.js +7 -0
  34. package/src/deployment/providers/gcp-vm.js +7 -0
  35. package/src/deployment/providers/kubernetes.js +30 -0
  36. package/src/deployment/providers/platforms/_shared.js +167 -0
  37. package/src/deployment/providers/platforms/aws-amplify.js +164 -0
  38. package/src/deployment/providers/platforms/azure-static-web-apps.js +68 -0
  39. package/src/deployment/providers/platforms/cloudflare-pages.js +103 -0
  40. package/src/deployment/providers/platforms/firebase-app-hosting.js +95 -0
  41. package/src/deployment/providers/platforms/firebase-hosting.js +99 -0
  42. package/src/deployment/providers/platforms/index.js +44 -0
  43. package/src/deployment/providers/platforms/netlify.js +102 -0
  44. package/src/deployment/providers/platforms/vercel.js +92 -0
  45. package/src/deployment/providers/ssh.js +365 -0
  46. package/src/detectors/angular.js +23 -0
  47. package/src/detectors/backend.detector.js +304 -0
  48. package/src/detectors/dotnet.js +18 -0
  49. package/src/detectors/frontend.detector.js +219 -0
  50. package/src/detectors/go.js +20 -0
  51. package/src/detectors/index.js +78 -0
  52. package/src/detectors/java.js +24 -0
  53. package/src/detectors/nextjs.js +23 -0
  54. package/src/detectors/node.js +28 -0
  55. package/src/detectors/php.js +17 -0
  56. package/src/detectors/python.js +22 -0
  57. package/src/detectors/react.js +29 -0
  58. package/src/detectors/vue.js +23 -0
  59. package/src/logger/index.js +44 -0
  60. package/src/notifications/email.js +53 -0
  61. package/src/notifications/index.js +34 -0
  62. package/src/notifications/slack.js +18 -0
  63. package/src/notifications/webhook.js +21 -0
  64. package/src/rollback/engine.js +102 -0
  65. package/src/storage/index.js +109 -0
  66. package/src/storage/providers/aws.js +96 -0
  67. package/src/storage/providers/azure.js +45 -0
  68. package/src/storage/providers/dropbox.js +49 -0
  69. package/src/storage/providers/ftp.js +69 -0
  70. package/src/storage/providers/gcp.js +45 -0
  71. package/src/storage/providers/gdrive.js +80 -0
  72. package/src/storage/providers/local.js +61 -0
  73. package/src/utils/author.js +141 -0
  74. package/src/utils/checksums.js +53 -0
  75. package/src/utils/firebase-config-generator.js +35 -0
  76. package/src/utils/github-actions.js +389 -0
  77. package/src/utils/init-platform.js +229 -0
  78. package/src/utils/nginx.js +34 -0
  79. package/src/utils/platform-env.js +132 -0
  80. package/src/utils/version.js +31 -0
@@ -0,0 +1,304 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * @typedef {Object} BackendDetectorResult
6
+ * @property {'backend'} projectType
7
+ * @property {string} framework
8
+ * @property {string} language
9
+ * @property {string|null} buildCommand
10
+ * @property {string|null} startCommand
11
+ * @property {string} buildOutput
12
+ * @property {string|null} testCommand
13
+ * @property {boolean} hasDocker
14
+ * @property {number} port
15
+ */
16
+
17
+ /** @type {Record<string, { detect: (cwd: string) => boolean, defaults: Omit<BackendDetectorResult, 'projectType'|'framework'|'hasDocker'> }>} */
18
+ const FRAMEWORKS = {
19
+ nestjs: {
20
+ detect: (cwd) => hasPackageDep(cwd, '@nestjs/core'),
21
+ defaults: {
22
+ language: 'node',
23
+ buildCommand: 'nest build',
24
+ startCommand: 'node dist/main',
25
+ buildOutput: 'dist',
26
+ testCommand: 'npm test',
27
+ port: 3000,
28
+ },
29
+ },
30
+ express: {
31
+ detect: (cwd) => hasPackageDep(cwd, 'express'),
32
+ defaults: {
33
+ language: 'node',
34
+ buildCommand: null,
35
+ startCommand: 'npm start',
36
+ buildOutput: '.',
37
+ testCommand: 'npm test',
38
+ port: 3000,
39
+ },
40
+ },
41
+ fastify: {
42
+ detect: (cwd) => hasPackageDep(cwd, 'fastify'),
43
+ defaults: {
44
+ language: 'node',
45
+ buildCommand: null,
46
+ startCommand: 'npm start',
47
+ buildOutput: '.',
48
+ testCommand: 'npm test',
49
+ port: 3000,
50
+ },
51
+ },
52
+ koa: {
53
+ detect: (cwd) => hasPackageDep(cwd, 'koa'),
54
+ defaults: {
55
+ language: 'node',
56
+ buildCommand: null,
57
+ startCommand: 'npm start',
58
+ buildOutput: '.',
59
+ testCommand: 'npm test',
60
+ port: 3000,
61
+ },
62
+ },
63
+ fastapi: {
64
+ detect: (cwd) => fileContains(cwd, 'requirements.txt', 'fastapi'),
65
+ defaults: {
66
+ language: 'python',
67
+ buildCommand: null,
68
+ startCommand: 'uvicorn main:app --host 0.0.0.0 --port 8000',
69
+ buildOutput: '.',
70
+ testCommand: 'pytest',
71
+ port: 8000,
72
+ },
73
+ },
74
+ django: {
75
+ detect: (cwd) => fileContains(cwd, 'requirements.txt', 'django'),
76
+ defaults: {
77
+ language: 'python',
78
+ buildCommand: null,
79
+ startCommand: 'gunicorn config.wsgi:application --bind 0.0.0.0:8000',
80
+ buildOutput: '.',
81
+ testCommand: 'python manage.py test',
82
+ port: 8000,
83
+ },
84
+ },
85
+ flask: {
86
+ detect: (cwd) => fileContains(cwd, 'requirements.txt', 'flask'),
87
+ defaults: {
88
+ language: 'python',
89
+ buildCommand: null,
90
+ startCommand: 'gunicorn app:app --bind 0.0.0.0:5000',
91
+ buildOutput: '.',
92
+ testCommand: 'pytest',
93
+ port: 5000,
94
+ },
95
+ },
96
+ laravel: {
97
+ detect: (cwd) => hasComposerPackage(cwd, 'laravel/framework'),
98
+ defaults: {
99
+ language: 'php',
100
+ buildCommand: null,
101
+ startCommand: 'php artisan serve',
102
+ buildOutput: '.',
103
+ testCommand: 'php artisan test',
104
+ port: 80,
105
+ },
106
+ },
107
+ symfony: {
108
+ detect: (cwd) => hasComposerPackage(cwd, 'symfony/framework-bundle'),
109
+ defaults: {
110
+ language: 'php',
111
+ buildCommand: null,
112
+ startCommand: 'php-fpm',
113
+ buildOutput: '.',
114
+ testCommand: 'php bin/phpunit',
115
+ port: 80,
116
+ },
117
+ },
118
+ spring: {
119
+ detect: (cwd) => fileContains(cwd, 'pom.xml', 'spring-boot'),
120
+ defaults: {
121
+ language: 'java',
122
+ buildCommand: 'mvn package',
123
+ startCommand: 'java -jar target/*.jar',
124
+ buildOutput: 'target',
125
+ testCommand: 'mvn test',
126
+ port: 8080,
127
+ },
128
+ },
129
+ go: {
130
+ detect: (cwd) => fs.existsSync(path.join(cwd, 'go.mod')),
131
+ defaults: {
132
+ language: 'go',
133
+ buildCommand: 'go build -o bin/app .',
134
+ startCommand: './bin/app',
135
+ buildOutput: 'bin',
136
+ testCommand: 'go test ./...',
137
+ port: 8080,
138
+ },
139
+ },
140
+ dotnet: {
141
+ detect: (cwd) => {
142
+ try {
143
+ return fs.readdirSync(cwd).some((f) => f.endsWith('.csproj'));
144
+ } catch {
145
+ return false;
146
+ }
147
+ },
148
+ defaults: {
149
+ language: 'dotnet',
150
+ buildCommand: 'dotnet publish -c Release -o publish',
151
+ startCommand: 'dotnet App.dll',
152
+ buildOutput: 'publish',
153
+ testCommand: 'dotnet test',
154
+ port: 5000,
155
+ },
156
+ },
157
+ rails: {
158
+ detect: (cwd) => fileContains(cwd, 'Gemfile', 'rails'),
159
+ defaults: {
160
+ language: 'ruby',
161
+ buildCommand: 'bundle exec rake assets:precompile',
162
+ startCommand: 'bundle exec puma',
163
+ buildOutput: '.',
164
+ testCommand: 'bundle exec rspec',
165
+ port: 3000,
166
+ },
167
+ },
168
+ };
169
+
170
+ const DETECTION_ORDER = [
171
+ 'nestjs',
172
+ 'express',
173
+ 'fastify',
174
+ 'koa',
175
+ 'fastapi',
176
+ 'django',
177
+ 'flask',
178
+ 'laravel',
179
+ 'symfony',
180
+ 'spring',
181
+ 'go',
182
+ 'dotnet',
183
+ 'rails',
184
+ ];
185
+
186
+ /**
187
+ * @param {string} cwd
188
+ * @param {string} dep
189
+ */
190
+ function hasPackageDep(cwd, dep) {
191
+ const pkgPath = path.join(cwd, 'package.json');
192
+ if (!fs.existsSync(pkgPath)) return false;
193
+ const pkg = fs.readJsonSync(pkgPath);
194
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
195
+ return !!deps[dep];
196
+ }
197
+
198
+ /**
199
+ * @param {string} cwd
200
+ * @param {string} filename
201
+ * @param {string} needle
202
+ */
203
+ function fileContains(cwd, filename, needle) {
204
+ const filePath = path.join(cwd, filename);
205
+ if (!fs.existsSync(filePath)) return false;
206
+ const content = fs.readFileSync(filePath, 'utf-8').toLowerCase();
207
+ return content.includes(needle.toLowerCase());
208
+ }
209
+
210
+ /**
211
+ * @param {string} cwd
212
+ * @param {string} packageName
213
+ */
214
+ function hasComposerPackage(cwd, packageName) {
215
+ const composerPath = path.join(cwd, 'composer.json');
216
+ if (!fs.existsSync(composerPath)) return false;
217
+ const composer = fs.readJsonSync(composerPath);
218
+ const deps = { ...composer.require, ...composer['require-dev'] };
219
+ return !!deps[packageName];
220
+ }
221
+
222
+ /**
223
+ * @param {string} [cwd]
224
+ * @returns {string|null}
225
+ */
226
+ export function detectBackendFramework(cwd = process.cwd()) {
227
+ for (const key of DETECTION_ORDER) {
228
+ if (FRAMEWORKS[key].detect(cwd)) return key;
229
+ }
230
+ return null;
231
+ }
232
+
233
+ /**
234
+ * @param {string} framework
235
+ * @param {string} [cwd]
236
+ * @returns {BackendDetectorResult}
237
+ */
238
+ export function getBackendInfo(framework, cwd = process.cwd()) {
239
+ const hasDocker = fs.existsSync(path.join(cwd, 'Dockerfile'));
240
+ const def = FRAMEWORKS[framework];
241
+
242
+ if (!def) {
243
+ return {
244
+ projectType: 'backend',
245
+ framework,
246
+ language: 'node',
247
+ buildCommand: null,
248
+ startCommand: 'npm start',
249
+ buildOutput: '.',
250
+ testCommand: 'npm test',
251
+ hasDocker,
252
+ port: 3000,
253
+ };
254
+ }
255
+
256
+ const pkgPath = path.join(cwd, 'package.json');
257
+ const scripts = fs.existsSync(pkgPath)
258
+ ? fs.readJsonSync(pkgPath).scripts || {}
259
+ : {};
260
+
261
+ let buildCommand = def.defaults.buildCommand;
262
+ let startCommand = def.defaults.startCommand;
263
+ let testCommand = def.defaults.testCommand;
264
+
265
+ if (def.defaults.language === 'node') {
266
+ if (scripts.build) buildCommand = 'npm run build';
267
+ if (scripts.start) startCommand = 'npm start';
268
+ if (scripts.test) testCommand = 'npm test';
269
+ }
270
+
271
+ return {
272
+ projectType: 'backend',
273
+ framework,
274
+ language: def.defaults.language,
275
+ buildCommand,
276
+ startCommand,
277
+ buildOutput: def.defaults.buildOutput,
278
+ testCommand,
279
+ hasDocker,
280
+ port: def.defaults.port,
281
+ };
282
+ }
283
+
284
+ /**
285
+ * @param {string} [cwd]
286
+ * @returns {BackendDetectorResult|null}
287
+ */
288
+ export function detectBackend(cwd = process.cwd()) {
289
+ const framework = detectBackendFramework(cwd);
290
+ if (!framework) return null;
291
+ return getBackendInfo(framework, cwd);
292
+ }
293
+
294
+ /** @type {Record<string, BackendDetectorResult>} */
295
+ export const BACKEND_FRAMEWORK_DEFAULTS = Object.fromEntries(
296
+ DETECTION_ORDER.map((key) => [key, getBackendInfo(key)])
297
+ );
298
+
299
+ export default {
300
+ detectBackend,
301
+ detectBackendFramework,
302
+ getBackendInfo,
303
+ BACKEND_FRAMEWORK_DEFAULTS,
304
+ };
@@ -0,0 +1,18 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ function detect(cwd = process.cwd()) {
5
+ const files = fs.readdirSync(cwd);
6
+ return files.some((f) => f.endsWith('.csproj') || f.endsWith('.sln'));
7
+ }
8
+
9
+ function getInfo(cwd = process.cwd()) {
10
+ return {
11
+ framework: 'dotnet',
12
+ buildCommand: 'dotnet build -c Release',
13
+ buildOutput: 'bin/Release',
14
+ hasDocker: fs.existsSync(path.join(cwd, 'Dockerfile')),
15
+ };
16
+ }
17
+
18
+ export default { detect, getInfo };
@@ -0,0 +1,219 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * @typedef {Object} DetectorResult
6
+ * @property {'frontend'} projectType
7
+ * @property {string} framework
8
+ * @property {string} language
9
+ * @property {string|null} buildCommand
10
+ * @property {string|null} startCommand
11
+ * @property {string} buildOutput
12
+ * @property {string|null} testCommand
13
+ * @property {boolean} hasDocker
14
+ * @property {number} port
15
+ */
16
+
17
+ /** @type {Record<string, { deps?: string[], files?: string[], defaults: Omit<DetectorResult, 'projectType'|'framework'|'hasDocker'> & { buildCommand: string|null, startCommand: string|null, testCommand: string|null } }>} */
18
+ const FRAMEWORKS = {
19
+ nextjs: {
20
+ deps: ['next'],
21
+ defaults: {
22
+ language: 'node',
23
+ buildCommand: 'npm run build',
24
+ startCommand: 'npm start',
25
+ buildOutput: '.next',
26
+ testCommand: 'npm test',
27
+ port: 3000,
28
+ },
29
+ },
30
+ react: {
31
+ deps: ['react'],
32
+ excludeDeps: ['next'],
33
+ defaults: {
34
+ language: 'node',
35
+ buildCommand: 'npm run build',
36
+ startCommand: null,
37
+ buildOutput: 'dist',
38
+ testCommand: 'npm test',
39
+ port: 80,
40
+ },
41
+ },
42
+ vue: {
43
+ deps: ['vue'],
44
+ defaults: {
45
+ language: 'node',
46
+ buildCommand: 'npm run build',
47
+ startCommand: null,
48
+ buildOutput: 'dist',
49
+ testCommand: 'npm test',
50
+ port: 80,
51
+ },
52
+ },
53
+ angular: {
54
+ deps: ['@angular/core'],
55
+ defaults: {
56
+ language: 'node',
57
+ buildCommand: 'ng build',
58
+ startCommand: null,
59
+ buildOutput: 'dist',
60
+ testCommand: 'npm test',
61
+ port: 80,
62
+ },
63
+ },
64
+ svelte: {
65
+ deps: ['svelte'],
66
+ defaults: {
67
+ language: 'node',
68
+ buildCommand: 'npm run build',
69
+ startCommand: null,
70
+ buildOutput: 'public',
71
+ testCommand: 'npm test',
72
+ port: 80,
73
+ },
74
+ },
75
+ astro: {
76
+ deps: ['astro'],
77
+ defaults: {
78
+ language: 'node',
79
+ buildCommand: 'astro build',
80
+ startCommand: null,
81
+ buildOutput: 'dist',
82
+ testCommand: 'npm test',
83
+ port: 80,
84
+ },
85
+ },
86
+ };
87
+
88
+ const DETECTION_ORDER = ['nextjs', 'react', 'vue', 'angular', 'svelte', 'astro'];
89
+
90
+ /**
91
+ * @param {string} cwd
92
+ * @returns {Record<string, string>|null}
93
+ */
94
+ function readPackageDeps(cwd) {
95
+ const pkgPath = path.join(cwd, 'package.json');
96
+ if (!fs.existsSync(pkgPath)) return null;
97
+ const pkg = fs.readJsonSync(pkgPath);
98
+ return { ...pkg.dependencies, ...pkg.devDependencies };
99
+ }
100
+
101
+ /**
102
+ * @param {string} cwd
103
+ * @param {string} frameworkKey
104
+ * @returns {boolean}
105
+ */
106
+ function matchesFramework(cwd, frameworkKey) {
107
+ const def = FRAMEWORKS[frameworkKey];
108
+ if (!def) return false;
109
+
110
+ if (def.deps) {
111
+ const deps = readPackageDeps(cwd);
112
+ if (!deps) return false;
113
+ if (def.excludeDeps?.some((d) => deps[d])) return false;
114
+ return def.deps.some((d) => deps[d]);
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ /**
121
+ * @param {string} cwd
122
+ * @returns {boolean}
123
+ */
124
+ function isVanilla(cwd) {
125
+ if (!fs.existsSync(path.join(cwd, 'index.html'))) return false;
126
+ const deps = readPackageDeps(cwd);
127
+ if (!deps) return true;
128
+ const majorFrameworks = ['react', 'vue', '@angular/core', 'next', 'svelte', 'astro'];
129
+ return !majorFrameworks.some((d) => deps[d]);
130
+ }
131
+
132
+ /**
133
+ * @param {string} [cwd]
134
+ * @returns {string|null}
135
+ */
136
+ export function detectFrontendFramework(cwd = process.cwd()) {
137
+ for (const key of DETECTION_ORDER) {
138
+ if (matchesFramework(cwd, key)) return key;
139
+ }
140
+ if (isVanilla(cwd)) return 'vanilla';
141
+ return null;
142
+ }
143
+
144
+ /**
145
+ * @param {string} framework
146
+ * @param {string} [cwd]
147
+ * @returns {DetectorResult}
148
+ */
149
+ export function getFrontendInfo(framework, cwd = process.cwd()) {
150
+ const hasDocker = fs.existsSync(path.join(cwd, 'Dockerfile'));
151
+ const pkgPath = path.join(cwd, 'package.json');
152
+ const scripts = fs.existsSync(pkgPath)
153
+ ? fs.readJsonSync(pkgPath).scripts || {}
154
+ : {};
155
+
156
+ if (framework === 'vanilla') {
157
+ return {
158
+ projectType: 'frontend',
159
+ framework: 'vanilla',
160
+ language: 'node',
161
+ buildCommand: null,
162
+ startCommand: null,
163
+ buildOutput: '.',
164
+ testCommand: scripts.test ? 'npm test' : null,
165
+ hasDocker,
166
+ port: 80,
167
+ };
168
+ }
169
+
170
+ const def = FRAMEWORKS[framework];
171
+ if (!def) {
172
+ return {
173
+ projectType: 'frontend',
174
+ framework,
175
+ language: 'node',
176
+ buildCommand: scripts.build ? 'npm run build' : null,
177
+ startCommand: scripts.start ? 'npm start' : null,
178
+ buildOutput: 'dist',
179
+ testCommand: scripts.test ? 'npm test' : null,
180
+ hasDocker,
181
+ port: 80,
182
+ };
183
+ }
184
+
185
+ let buildOutput = def.defaults.buildOutput;
186
+ if (framework === 'react' || framework === 'vue') {
187
+ if (fs.existsSync(path.join(cwd, 'dist'))) buildOutput = 'dist';
188
+ else if (fs.existsSync(path.join(cwd, 'build'))) buildOutput = 'build';
189
+ }
190
+
191
+ return {
192
+ projectType: 'frontend',
193
+ framework,
194
+ language: def.defaults.language,
195
+ buildCommand: scripts.build ? `npm run build` : def.defaults.buildCommand,
196
+ startCommand: scripts.start ? 'npm start' : def.defaults.startCommand,
197
+ buildOutput,
198
+ testCommand: scripts.test ? 'npm test' : def.defaults.testCommand,
199
+ hasDocker,
200
+ port: def.defaults.port,
201
+ };
202
+ }
203
+
204
+ /**
205
+ * @param {string} [cwd]
206
+ * @returns {DetectorResult|null}
207
+ */
208
+ export function detectFrontend(cwd = process.cwd()) {
209
+ const framework = detectFrontendFramework(cwd);
210
+ if (!framework) return null;
211
+ return getFrontendInfo(framework, cwd);
212
+ }
213
+
214
+ /** @type {Record<string, DetectorResult>} */
215
+ export const FRONTEND_FRAMEWORK_DEFAULTS = Object.fromEntries(
216
+ [...DETECTION_ORDER, 'vanilla'].map((key) => [key, getFrontendInfo(key)])
217
+ );
218
+
219
+ export default { detectFrontend, detectFrontendFramework, getFrontendInfo, FRONTEND_FRAMEWORK_DEFAULTS };
@@ -0,0 +1,20 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ function detect(cwd = process.cwd()) {
5
+ return (
6
+ fs.existsSync(path.join(cwd, 'go.mod')) ||
7
+ fs.existsSync(path.join(cwd, 'main.go'))
8
+ );
9
+ }
10
+
11
+ function getInfo(cwd = process.cwd()) {
12
+ return {
13
+ framework: 'go',
14
+ buildCommand: 'go build -o bin/app .',
15
+ buildOutput: 'bin',
16
+ hasDocker: fs.existsSync(path.join(cwd, 'Dockerfile')),
17
+ };
18
+ }
19
+
20
+ export default { detect, getInfo };
@@ -0,0 +1,78 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { detectFrontend } from './frontend.detector.js';
4
+ import { detectBackend } from './backend.detector.js';
5
+
6
+ /**
7
+ * @typedef {Object} DetectionResult
8
+ * @property {'frontend'|'backend'} [projectType]
9
+ * @property {string} framework
10
+ * @property {string} [language]
11
+ * @property {string|null} [buildCommand]
12
+ * @property {string|null} [startCommand]
13
+ * @property {string} buildOutput
14
+ * @property {string|null} [testCommand]
15
+ * @property {boolean} hasDocker
16
+ * @property {number} [port]
17
+ */
18
+
19
+ /**
20
+ * @param {string} [cwd]
21
+ * @returns {Promise<DetectionResult|null>}
22
+ */
23
+ export async function detectFramework(cwd = process.cwd()) {
24
+ const frontend = detectFrontend(cwd);
25
+ if (frontend) return frontend;
26
+
27
+ const backend = detectBackend(cwd);
28
+ if (backend) return backend;
29
+
30
+ const legacyDetectors = [
31
+ (await import('./react.js')).default,
32
+ (await import('./vue.js')).default,
33
+ (await import('./angular.js')).default,
34
+ (await import('./nextjs.js')).default,
35
+ (await import('./node.js')).default,
36
+ (await import('./python.js')).default,
37
+ (await import('./php.js')).default,
38
+ (await import('./java.js')).default,
39
+ (await import('./go.js')).default,
40
+ (await import('./dotnet.js')).default,
41
+ ];
42
+
43
+ for (const detector of legacyDetectors) {
44
+ if (detector.detect(cwd)) {
45
+ const info = detector.getInfo(cwd);
46
+ return {
47
+ ...info,
48
+ projectType: info.framework === 'node' ? 'backend' : 'frontend',
49
+ language: info.framework,
50
+ startCommand: null,
51
+ testCommand: 'npm test',
52
+ port: 3000,
53
+ };
54
+ }
55
+ }
56
+
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * @param {string} [cwd]
62
+ * @returns {Promise<DetectionResult|null>}
63
+ */
64
+ export async function detectProjectType(cwd = process.cwd()) {
65
+ return detectFramework(cwd);
66
+ }
67
+
68
+ /**
69
+ * @param {string} [cwd]
70
+ * @returns {Promise<boolean>}
71
+ */
72
+ export async function hasDockerfile(cwd = process.cwd()) {
73
+ return fs.pathExists(path.join(cwd, 'Dockerfile'));
74
+ }
75
+
76
+ export { detectFrontend, detectBackend };
77
+
78
+ export default { detectFramework, detectProjectType, hasDockerfile, detectFrontend, detectBackend };
@@ -0,0 +1,24 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ function detect(cwd = process.cwd()) {
5
+ return (
6
+ fs.existsSync(path.join(cwd, 'pom.xml')) ||
7
+ fs.existsSync(path.join(cwd, 'build.gradle')) ||
8
+ fs.existsSync(path.join(cwd, 'build.gradle.kts'))
9
+ );
10
+ }
11
+
12
+ function getInfo(cwd = process.cwd()) {
13
+ const isGradle =
14
+ fs.existsSync(path.join(cwd, 'build.gradle')) ||
15
+ fs.existsSync(path.join(cwd, 'build.gradle.kts'));
16
+ return {
17
+ framework: 'java',
18
+ buildCommand: isGradle ? './gradlew build' : 'mvn package',
19
+ buildOutput: 'target',
20
+ hasDocker: fs.existsSync(path.join(cwd, 'Dockerfile')),
21
+ };
22
+ }
23
+
24
+ export default { detect, getInfo };
@@ -0,0 +1,23 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ function detect(cwd = process.cwd()) {
5
+ const pkgPath = path.join(cwd, 'package.json');
6
+ if (!fs.existsSync(pkgPath)) return false;
7
+ const pkg = fs.readJsonSync(pkgPath);
8
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
9
+ return !!deps.next;
10
+ }
11
+
12
+ function getInfo(cwd = process.cwd()) {
13
+ const pkg = fs.readJsonSync(path.join(cwd, 'package.json'));
14
+ const scripts = pkg.scripts || {};
15
+ return {
16
+ framework: 'nextjs',
17
+ buildCommand: scripts.build ? 'npm run build' : 'npm run build',
18
+ buildOutput: '.next',
19
+ hasDocker: fs.existsSync(path.join(cwd, 'Dockerfile')),
20
+ };
21
+ }
22
+
23
+ export default { detect, getInfo };