@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.
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/install.ps1 +55 -0
- package/install.sh +99 -0
- package/package.json +86 -0
- package/src/adapters/dotnet.adapter.js +41 -0
- package/src/adapters/go.adapter.js +40 -0
- package/src/adapters/index.js +48 -0
- package/src/adapters/java.adapter.js +46 -0
- package/src/adapters/node.adapter.js +77 -0
- package/src/adapters/php.adapter.js +43 -0
- package/src/adapters/python.adapter.js +54 -0
- package/src/adapters/rails.adapter.js +66 -0
- package/src/artifact/engine.js +473 -0
- package/src/cli/index.js +45 -0
- package/src/commands/artifact.js +88 -0
- package/src/commands/build.js +44 -0
- package/src/commands/clean.js +50 -0
- package/src/commands/deploy.js +82 -0
- package/src/commands/doctor.js +630 -0
- package/src/commands/init.js +795 -0
- package/src/commands/logs.js +33 -0
- package/src/commands/rollback.js +50 -0
- package/src/commands/storage.js +116 -0
- package/src/commands/update.js +63 -0
- package/src/commands/verify.js +55 -0
- package/src/core/config.js +168 -0
- package/src/core/pipeline.js +77 -0
- package/src/core/stages.js +210 -0
- package/src/deployment/index.js +156 -0
- package/src/deployment/providers/azure-vm.js +7 -0
- package/src/deployment/providers/docker.js +30 -0
- package/src/deployment/providers/ec2.js +7 -0
- package/src/deployment/providers/gcp-vm.js +7 -0
- package/src/deployment/providers/kubernetes.js +30 -0
- package/src/deployment/providers/platforms/_shared.js +167 -0
- package/src/deployment/providers/platforms/aws-amplify.js +164 -0
- package/src/deployment/providers/platforms/azure-static-web-apps.js +68 -0
- package/src/deployment/providers/platforms/cloudflare-pages.js +103 -0
- package/src/deployment/providers/platforms/firebase-app-hosting.js +95 -0
- package/src/deployment/providers/platforms/firebase-hosting.js +99 -0
- package/src/deployment/providers/platforms/index.js +44 -0
- package/src/deployment/providers/platforms/netlify.js +102 -0
- package/src/deployment/providers/platforms/vercel.js +92 -0
- package/src/deployment/providers/ssh.js +365 -0
- package/src/detectors/angular.js +23 -0
- package/src/detectors/backend.detector.js +304 -0
- package/src/detectors/dotnet.js +18 -0
- package/src/detectors/frontend.detector.js +219 -0
- package/src/detectors/go.js +20 -0
- package/src/detectors/index.js +78 -0
- package/src/detectors/java.js +24 -0
- package/src/detectors/nextjs.js +23 -0
- package/src/detectors/node.js +28 -0
- package/src/detectors/php.js +17 -0
- package/src/detectors/python.js +22 -0
- package/src/detectors/react.js +29 -0
- package/src/detectors/vue.js +23 -0
- package/src/logger/index.js +44 -0
- package/src/notifications/email.js +53 -0
- package/src/notifications/index.js +34 -0
- package/src/notifications/slack.js +18 -0
- package/src/notifications/webhook.js +21 -0
- package/src/rollback/engine.js +102 -0
- package/src/storage/index.js +109 -0
- package/src/storage/providers/aws.js +96 -0
- package/src/storage/providers/azure.js +45 -0
- package/src/storage/providers/dropbox.js +49 -0
- package/src/storage/providers/ftp.js +69 -0
- package/src/storage/providers/gcp.js +45 -0
- package/src/storage/providers/gdrive.js +80 -0
- package/src/storage/providers/local.js +61 -0
- package/src/utils/author.js +141 -0
- package/src/utils/checksums.js +53 -0
- package/src/utils/firebase-config-generator.js +35 -0
- package/src/utils/github-actions.js +389 -0
- package/src/utils/init-platform.js +229 -0
- package/src/utils/nginx.js +34 -0
- package/src/utils/platform-env.js +132 -0
- 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 };
|