project-compass 4.2.1 โ 4.3.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/package.json +28 -3
- package/src/cli.js +91 -21
- package/src/components/Footer.js +64 -8
- package/src/components/Header.js +47 -8
- package/src/components/Navigator.js +69 -15
- package/src/detectors/dotnet.js +110 -5
- package/src/detectors/frameworks.js +692 -42
- package/src/detectors/go.js +111 -10
- package/src/detectors/java.js +129 -14
- package/src/detectors/node.js +69 -11
- package/src/detectors/php.js +98 -5
- package/src/detectors/python.js +137 -39
- package/src/detectors/ruby.js +105 -5
- package/src/detectors/rust.js +111 -10
- package/src/detectors/utils.js +95 -7
package/src/detectors/go.js
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import path from 'path';
|
|
2
|
-
import { checkBinary } from './utils.js';
|
|
3
|
+
import { checkBinary, hasProjectFile } from './utils.js';
|
|
4
|
+
|
|
5
|
+
function parseGoMod(content) {
|
|
6
|
+
const metadata = {
|
|
7
|
+
module: '',
|
|
8
|
+
goVersion: '',
|
|
9
|
+
dependencies: []
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const lines = content.split('\n');
|
|
13
|
+
let inRequire = false;
|
|
14
|
+
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
|
|
18
|
+
if (trimmed.startsWith('module ')) {
|
|
19
|
+
metadata.module = trimmed.split(/\s+/)[1]?.replace(/"/g, '') || '';
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.startsWith('go ')) {
|
|
22
|
+
metadata.goVersion = trimmed.split(/\s+/)[1] || '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (trimmed === 'require (') {
|
|
26
|
+
inRequire = true;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (trimmed === ')') {
|
|
30
|
+
inRequire = false;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (inRequire || (trimmed.includes(' ') && !trimmed.startsWith('//'))) {
|
|
35
|
+
const parts = trimmed.split(/\s+/);
|
|
36
|
+
if (parts[0] && !parts[0].startsWith('//')) {
|
|
37
|
+
metadata.dependencies.push(parts[0].replace(/"/g, ''));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return metadata;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function detectGoFrameworks(deps) {
|
|
46
|
+
const frameworks = [];
|
|
47
|
+
const depStr = deps.join(' ').toLowerCase();
|
|
48
|
+
|
|
49
|
+
if (depStr.includes('gin') || depStr.includes('gin-gonic')) frameworks.push({ name: 'Gin', icon: '๐ธ' });
|
|
50
|
+
if (depStr.includes('echo') || depStr.includes('labstack/echo')) frameworks.push({ name: 'Echo', icon: '๐' });
|
|
51
|
+
if (depStr.includes('fiber') || depStr.includes('gofiber')) frameworks.push({ name: 'Fiber', icon: '๐ฅ' });
|
|
52
|
+
if (depStr.includes('chi')) frameworks.push({ name: 'Chi', icon: '๐ค' });
|
|
53
|
+
if (depStr.includes('gorilla')) frameworks.push({ name: 'Gorilla', icon: '๐ฆ' });
|
|
54
|
+
if (depStr.includes('iris')) frameworks.push({ name: 'Iris', icon: '๐บ' });
|
|
55
|
+
if (depStr.includes('beego')) frameworks.push({ name: 'Beego', icon: '๐' });
|
|
56
|
+
if (depStr.includes('revel')) frameworks.push({ name: 'Revel', icon: '๐' });
|
|
57
|
+
if (depStr.includes('gqlgen')) frameworks.push({ name: 'GQLGen', icon: 'โผ๏ธ' });
|
|
58
|
+
if (depStr.includes('grpc')) frameworks.push({ name: 'gRPC', icon: '๐' });
|
|
59
|
+
|
|
60
|
+
return frameworks;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function findGoEntry(projectPath) {
|
|
64
|
+
const possibleEntries = ['main.go', 'cmd/main.go', 'app.go', 'server.go'];
|
|
65
|
+
for (const entry of possibleEntries) {
|
|
66
|
+
if (hasProjectFile(projectPath, entry)) return entry;
|
|
67
|
+
}
|
|
68
|
+
return 'main.go';
|
|
69
|
+
}
|
|
3
70
|
|
|
4
71
|
export default {
|
|
5
72
|
type: 'go',
|
|
@@ -10,25 +77,59 @@ export default {
|
|
|
10
77
|
binaries: ['go'],
|
|
11
78
|
async build(projectPath, manifest) {
|
|
12
79
|
const missingBinaries = this.binaries.filter(b => !checkBinary(b));
|
|
80
|
+
let metadata = { module: '', goVersion: '', dependencies: [] };
|
|
81
|
+
let frameworks = [];
|
|
82
|
+
let entryPoint = 'main.go';
|
|
83
|
+
|
|
84
|
+
const goModPath = path.join(projectPath, 'go.mod');
|
|
85
|
+
if (fs.existsSync(goModPath)) {
|
|
86
|
+
const content = fs.readFileSync(goModPath, 'utf-8');
|
|
87
|
+
metadata = parseGoMod(content);
|
|
88
|
+
frameworks = detectGoFrameworks(metadata.dependencies);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
entryPoint = findGoEntry(projectPath);
|
|
92
|
+
|
|
93
|
+
const commands = {
|
|
94
|
+
install: { label: 'Go tidy', command: ['go', 'mod', 'tidy'], source: 'builtin' },
|
|
95
|
+
build: { label: 'Go build', command: ['go', 'build', '-o', 'app', '.'], source: 'builtin' },
|
|
96
|
+
test: { label: 'Go test', command: ['go', 'test', './...'], source: 'builtin' },
|
|
97
|
+
run: { label: 'Go run', command: ['go', 'run', entryPoint], source: 'builtin' },
|
|
98
|
+
fmt: { label: 'Go fmt', command: ['go', 'fmt', './...'], source: 'builtin' },
|
|
99
|
+
vet: { label: 'Go vet', command: ['go', 'vet', './...'], source: 'builtin' }
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const setupHints = [];
|
|
103
|
+
if (missingBinaries.length > 0) {
|
|
104
|
+
setupHints.push('Install Go from https://go.dev/dl/');
|
|
105
|
+
}
|
|
106
|
+
if (metadata.dependencies.length > 0) {
|
|
107
|
+
setupHints.push('Run go mod tidy to ensure dependencies');
|
|
108
|
+
}
|
|
109
|
+
if (metadata.goVersion) {
|
|
110
|
+
setupHints.push(`Requires Go ${metadata.goVersion}+`);
|
|
111
|
+
}
|
|
112
|
+
|
|
13
113
|
return {
|
|
14
114
|
id: `${projectPath}::go`,
|
|
15
115
|
path: projectPath,
|
|
16
|
-
name: path.basename(projectPath),
|
|
116
|
+
name: metadata.module || path.basename(projectPath),
|
|
17
117
|
type: 'Go',
|
|
18
118
|
icon: '๐น',
|
|
19
119
|
priority: this.priority,
|
|
20
|
-
commands
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
120
|
+
commands,
|
|
121
|
+
metadata: {
|
|
122
|
+
...metadata,
|
|
123
|
+
packageManager: 'go',
|
|
124
|
+
entryPoint
|
|
25
125
|
},
|
|
26
|
-
metadata: {},
|
|
27
126
|
manifest: path.basename(manifest),
|
|
28
|
-
description: ''
|
|
127
|
+
description: frameworks.map(f => f.name).join(', ') || `Go ${metadata.goVersion || ''}`,
|
|
29
128
|
missingBinaries,
|
|
129
|
+
frameworks,
|
|
30
130
|
extra: {
|
|
31
|
-
setupHints
|
|
131
|
+
setupHints,
|
|
132
|
+
entryPoint
|
|
32
133
|
}
|
|
33
134
|
};
|
|
34
135
|
}
|
package/src/detectors/java.js
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import { checkBinary, hasProjectFile } from './utils.js';
|
|
3
4
|
|
|
5
|
+
function parsePomXml(content) {
|
|
6
|
+
const metadata = {
|
|
7
|
+
groupId: '',
|
|
8
|
+
artifactId: '',
|
|
9
|
+
version: '',
|
|
10
|
+
name: '',
|
|
11
|
+
description: '',
|
|
12
|
+
dependencies: []
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const groupIdMatch = content.match(/<groupId>([^<]+)<\/groupId>/);
|
|
16
|
+
if (groupIdMatch) metadata.groupId = groupIdMatch[1];
|
|
17
|
+
|
|
18
|
+
const artifactIdMatch = content.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
19
|
+
if (artifactIdMatch) metadata.artifactId = artifactIdMatch[1];
|
|
20
|
+
|
|
21
|
+
const versionMatch = content.match(/<version>([^<]+)<\/version>/);
|
|
22
|
+
if (versionMatch) metadata.version = versionMatch[1];
|
|
23
|
+
|
|
24
|
+
const nameMatch = content.match(/<name>([^<]+)<\/name>/);
|
|
25
|
+
if (nameMatch) metadata.name = nameMatch[1];
|
|
26
|
+
|
|
27
|
+
const descMatch = content.match(/<description>([^<]+)<\/description>/);
|
|
28
|
+
if (descMatch) metadata.description = descMatch[1];
|
|
29
|
+
|
|
30
|
+
const depMatches = content.matchAll(/<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g);
|
|
31
|
+
for (const match of depMatches) {
|
|
32
|
+
if (match[1]) metadata.dependencies.push(match[1]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return metadata;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseGradleBuild(content) {
|
|
39
|
+
const metadata = {
|
|
40
|
+
name: '',
|
|
41
|
+
version: '',
|
|
42
|
+
description: '',
|
|
43
|
+
dependencies: []
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const nameMatch = content.match(/rootProject\.name\s*=\s*['"]([^'"]+)['"]/);
|
|
47
|
+
if (nameMatch) metadata.name = nameMatch[1];
|
|
48
|
+
|
|
49
|
+
const versionMatch = content.match(/version\s*=\s*['"]([^'"]+)['"]/);
|
|
50
|
+
if (versionMatch) metadata.version = versionMatch[1];
|
|
51
|
+
|
|
52
|
+
const depMatches = content.matchAll(/implementation\s+['"]([^'"]+)['"]/g);
|
|
53
|
+
for (const match of depMatches) {
|
|
54
|
+
if (match[1]) metadata.dependencies.push(match[1].split(':')[1] || match[1]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return metadata;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function detectJavaFrameworks(deps) {
|
|
61
|
+
const frameworks = [];
|
|
62
|
+
const depStr = deps.join(' ').toLowerCase();
|
|
63
|
+
|
|
64
|
+
if (depStr.includes('spring-boot') || depStr.includes('springframework')) frameworks.push({ name: 'Spring Boot', icon: '๐' });
|
|
65
|
+
if (depStr.includes('quarkus')) frameworks.push({ name: 'Quarkus', icon: 'โก' });
|
|
66
|
+
if (depStr.includes('micronaut')) frameworks.push({ name: 'Micronaut', icon: '๐' });
|
|
67
|
+
if (depStr.includes('play')) frameworks.push({ name: 'Play Framework', icon: '๐ญ' });
|
|
68
|
+
if (depStr.includes('vertx')) frameworks.push({ name: 'Vert.x', icon: '๐' });
|
|
69
|
+
if (depStr.includes('dropwizard')) frameworks.push({ name: 'Dropwizard', icon: '๐' });
|
|
70
|
+
if (depStr.includes(('hibernate'))) frameworks.push({ name: 'Hibernate', icon: '๐๏ธ' });
|
|
71
|
+
if (depStr.includes('junit')) frameworks.push({ name: 'JUnit', icon: 'โ
' });
|
|
72
|
+
if (depStr.includes('lombok')) frameworks.push({ name: 'Lombok', icon: '๐ง' });
|
|
73
|
+
|
|
74
|
+
return frameworks;
|
|
75
|
+
}
|
|
76
|
+
|
|
4
77
|
export default {
|
|
5
78
|
type: 'java',
|
|
6
79
|
label: 'Java',
|
|
@@ -12,34 +85,76 @@ export default {
|
|
|
12
85
|
const missingBinaries = this.binaries.filter(b => !checkBinary(b));
|
|
13
86
|
const hasMvnw = hasProjectFile(projectPath, 'mvnw');
|
|
14
87
|
const hasGradlew = hasProjectFile(projectPath, 'gradlew');
|
|
88
|
+
const isMaven = hasProjectFile(projectPath, 'pom.xml');
|
|
89
|
+
const isGradle = hasProjectFile(projectPath, 'build.gradle') || hasProjectFile(projectPath, 'build.gradle.kts');
|
|
90
|
+
|
|
91
|
+
let metadata = { name: '', version: '', description: '', dependencies: [] };
|
|
92
|
+
let frameworks = [];
|
|
93
|
+
let buildTool = isGradle ? 'gradle' : 'maven';
|
|
94
|
+
|
|
95
|
+
if (isMaven) {
|
|
96
|
+
const pomPath = path.join(projectPath, 'pom.xml');
|
|
97
|
+
if (fs.existsSync(pomPath)) {
|
|
98
|
+
const content = fs.readFileSync(pomPath, 'utf-8');
|
|
99
|
+
metadata = parsePomXml(content);
|
|
100
|
+
frameworks = detectJavaFrameworks(metadata.dependencies);
|
|
101
|
+
}
|
|
102
|
+
} else if (isGradle) {
|
|
103
|
+
const gradleFile = hasProjectFile(projectPath, 'build.gradle.kts') ? 'build.gradle.kts' : 'build.gradle';
|
|
104
|
+
const gradlePath = path.join(projectPath, gradleFile);
|
|
105
|
+
if (fs.existsSync(gradlePath)) {
|
|
106
|
+
const content = fs.readFileSync(gradlePath, 'utf-8');
|
|
107
|
+
metadata = parseGradleBuild(content);
|
|
108
|
+
frameworks = detectJavaFrameworks(metadata.dependencies);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
15
112
|
const commands = {};
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
commands.
|
|
19
|
-
commands.
|
|
20
|
-
|
|
21
|
-
commands.
|
|
22
|
-
commands.
|
|
23
|
-
commands.test = { label: 'Maven test', command: ['./mvnw', 'test'] };
|
|
113
|
+
if (isGradle) {
|
|
114
|
+
const gradleCmd = hasGradlew ? ['./gradlew'] : ['gradle'];
|
|
115
|
+
commands.install = { label: 'Gradle dependencies', command: [...gradleCmd, 'dependencies'], source: 'builtin' };
|
|
116
|
+
commands.build = { label: 'Gradle build', command: [...gradleCmd, 'build'], source: 'builtin' };
|
|
117
|
+
commands.test = { label: 'Gradle test', command: [...gradleCmd, 'test'], source: 'builtin' };
|
|
118
|
+
commands.run = { label: 'Gradle run', command: [...gradleCmd, 'run'], source: 'builtin' };
|
|
119
|
+
commands.clean = { label: 'Gradle clean', command: [...gradleCmd, 'clean'], source: 'builtin' };
|
|
24
120
|
} else {
|
|
25
|
-
|
|
26
|
-
commands.
|
|
121
|
+
const mvnCmd = hasMvnw ? ['./mvnw'] : ['mvn'];
|
|
122
|
+
commands.install = { label: 'Maven install', command: [...mvnCmd, 'install'], source: 'builtin' };
|
|
123
|
+
commands.build = { label: 'Maven package', command: [...mvnCmd, 'package'], source: 'builtin' };
|
|
124
|
+
commands.test = { label: 'Maven test', command: [...mvnCmd, 'test'], source: 'builtin' };
|
|
125
|
+
commands.run = { label: 'Maven spring-boot:run', command: [...mvnCmd, 'spring-boot:run'], source: 'builtin' };
|
|
126
|
+
commands.clean = { label: 'Maven clean', command: [...mvnCmd, 'clean'], source: 'builtin' };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const setupHints = [];
|
|
130
|
+
if (missingBinaries.length > 0) {
|
|
131
|
+
setupHints.push('Install JDK 17+ from https://adoptium.net/');
|
|
132
|
+
}
|
|
133
|
+
if (isMaven) {
|
|
134
|
+
setupHints.push('Run ' + (hasMvnw ? './mvnw install' : 'mvn install') + ' to build');
|
|
135
|
+
} else if (isGradle) {
|
|
136
|
+
setupHints.push('Run ' + (hasGradlew ? './gradlew build' : 'gradle build') + ' to build');
|
|
27
137
|
}
|
|
28
138
|
|
|
29
139
|
return {
|
|
30
140
|
id: `${projectPath}::java`,
|
|
31
141
|
path: projectPath,
|
|
32
|
-
name: path.basename(projectPath),
|
|
142
|
+
name: metadata.name || metadata.artifactId || path.basename(projectPath),
|
|
33
143
|
type: 'Java',
|
|
34
144
|
icon: 'โ',
|
|
35
145
|
priority: this.priority,
|
|
36
146
|
commands,
|
|
37
|
-
metadata: {
|
|
147
|
+
metadata: {
|
|
148
|
+
...metadata,
|
|
149
|
+
packageManager: buildTool
|
|
150
|
+
},
|
|
38
151
|
manifest: path.basename(manifest),
|
|
39
|
-
description: '',
|
|
152
|
+
description: metadata.description || frameworks.map(f => f.name).join(', '),
|
|
40
153
|
missingBinaries,
|
|
154
|
+
frameworks,
|
|
41
155
|
extra: {
|
|
42
|
-
setupHints
|
|
156
|
+
setupHints,
|
|
157
|
+
buildTool
|
|
43
158
|
}
|
|
44
159
|
};
|
|
45
160
|
}
|
package/src/detectors/node.js
CHANGED
|
@@ -9,7 +9,43 @@ function gatherNodeDependencies(pkg) {
|
|
|
9
9
|
Object.keys(pkg[key]).forEach((name) => deps.add(name));
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
|
-
return Array.from(deps);
|
|
12
|
+
return Array.from(deps).map(dep => ({ name: dep, version: pkg.dependencies?.[dep] || pkg.devDependencies?.[dep] || 'latest' }));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function detectNodeProjectType(pkg) {
|
|
16
|
+
const deps = pkg.dependencies || {};
|
|
17
|
+
const devDeps = pkg.devDependencies || {};
|
|
18
|
+
const allDeps = { ...deps, ...devDeps };
|
|
19
|
+
|
|
20
|
+
if (allDeps['next']) return { type: 'Next.js', icon: '๐งญ' };
|
|
21
|
+
if (allDeps['react'] && (allDeps['react-scripts'] || allDeps['vite'])) return { type: 'React', icon: 'โ๏ธ' };
|
|
22
|
+
if (allDeps['vue']) return { type: 'Vue.js', icon: '๐ฉ' };
|
|
23
|
+
if (allDeps['@nestjs/core']) return { type: 'NestJS', icon: '๐ก๏ธ' };
|
|
24
|
+
if (allDeps['express']) return { type: 'Express', icon: '๐' };
|
|
25
|
+
if (allDeps['fastify']) return { type: 'Fastify', icon: 'โก' };
|
|
26
|
+
if (allDeps['koa']) return { type: 'Koa', icon: '๐' };
|
|
27
|
+
if (allDeps['@sveltejs/kit'] || allDeps['svelte']) return { type: 'Svelte', icon: '๐งก' };
|
|
28
|
+
if (allDeps['astro']) return { type: 'Astro', icon: '๐' };
|
|
29
|
+
if (allDeps['nuxt']) return { type: 'Nuxt', icon: '๐ข' };
|
|
30
|
+
if (allDeps['vite']) return { type: 'Vite', icon: 'โก' };
|
|
31
|
+
if (allDeps['electron']) return { type: 'Electron', icon: 'โ๏ธ' };
|
|
32
|
+
if (allDeps['typescript']) return { type: 'TypeScript', icon: '๐ท' };
|
|
33
|
+
return { type: 'Node.js', icon: '๐ข' };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function findEntryPoint(projectPath, pkg) {
|
|
37
|
+
const possibleEntries = [
|
|
38
|
+
'src/index.js', 'src/index.ts', 'index.js', 'index.ts',
|
|
39
|
+
'src/main.js', 'src/main.ts', 'main.js', 'main.ts',
|
|
40
|
+
'app.js', 'app.ts', 'server.js', 'server.ts'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
for (const entry of possibleEntries) {
|
|
44
|
+
if (hasProjectFile(projectPath, entry)) return entry;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (pkg.main) return pkg.main;
|
|
48
|
+
return null;
|
|
13
49
|
}
|
|
14
50
|
|
|
15
51
|
export default {
|
|
@@ -29,53 +65,75 @@ export default {
|
|
|
29
65
|
const pkg = JSON.parse(content);
|
|
30
66
|
const pm = getPackageManager(projectPath);
|
|
31
67
|
const scripts = pkg.scripts || {};
|
|
68
|
+
const projectType = detectNodeProjectType(pkg);
|
|
69
|
+
const entryPoint = findEntryPoint(projectPath, pkg);
|
|
70
|
+
|
|
32
71
|
const commands = {};
|
|
33
72
|
const preferScript = (targetKey, names, labelText) => {
|
|
34
73
|
for (const name of names) {
|
|
35
74
|
if (Object.prototype.hasOwnProperty.call(scripts, name)) {
|
|
36
|
-
commands[targetKey] = { label: labelText, command: [pm, 'run', name] };
|
|
75
|
+
commands[targetKey] = { label: labelText, command: [pm, 'run', name], source: 'builtin' };
|
|
37
76
|
break;
|
|
38
77
|
}
|
|
39
78
|
}
|
|
40
79
|
};
|
|
41
|
-
|
|
80
|
+
|
|
81
|
+
commands.install = { label: `${pm} install`, command: [pm, 'install'], source: 'builtin' };
|
|
42
82
|
preferScript('build', ['build', 'compile', 'dist'], 'Build');
|
|
43
83
|
preferScript('test', ['test', 'check', 'spec'], 'Test');
|
|
44
|
-
preferScript('
|
|
84
|
+
preferScript('dev', ['dev', 'start:dev'], 'Dev');
|
|
85
|
+
preferScript('run', ['start', 'serve', 'run'], 'Start');
|
|
86
|
+
|
|
87
|
+
if (entryPoint && !commands.run && !commands.dev) {
|
|
88
|
+
commands.run = { label: 'Run', command: ['node', entryPoint], source: 'builtin' };
|
|
89
|
+
}
|
|
90
|
+
|
|
45
91
|
if (Object.prototype.hasOwnProperty.call(scripts, 'lint')) {
|
|
46
|
-
commands.lint = { label: 'Lint', command: [pm, 'run', 'lint'] };
|
|
92
|
+
commands.lint = { label: 'Lint', command: [pm, 'run', 'lint'], source: 'builtin' };
|
|
93
|
+
}
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(scripts, 'format')) {
|
|
95
|
+
commands.format = { label: 'Format', command: [pm, 'run', 'format'], source: 'builtin' };
|
|
47
96
|
}
|
|
48
97
|
|
|
49
98
|
const metadata = {
|
|
50
99
|
dependencies: gatherNodeDependencies(pkg),
|
|
51
100
|
scripts,
|
|
52
101
|
packageJson: pkg,
|
|
53
|
-
packageManager:
|
|
102
|
+
packageManager: pm,
|
|
103
|
+
projectType: projectType.type,
|
|
104
|
+
entryPoint
|
|
54
105
|
};
|
|
55
106
|
|
|
56
107
|
const setupHints = [];
|
|
57
108
|
if (metadata.dependencies.length) {
|
|
58
|
-
setupHints.push(
|
|
59
|
-
if (hasProjectFile(projectPath, 'yarn.lock')) {
|
|
109
|
+
setupHints.push(`Run ${pm} install to fetch dependencies.`);
|
|
110
|
+
if (pm === 'npm' && hasProjectFile(projectPath, 'yarn.lock')) {
|
|
60
111
|
setupHints.push('Or run yarn install if you prefer Yarn.');
|
|
61
112
|
}
|
|
62
113
|
}
|
|
114
|
+
|
|
115
|
+
const workspaces = pkg.workspaces || [];
|
|
116
|
+
if (workspaces.length > 0) {
|
|
117
|
+
setupHints.push('This is a monorepo with workspaces: ' + workspaces.join(', '));
|
|
118
|
+
}
|
|
63
119
|
|
|
64
120
|
return {
|
|
65
121
|
id: `${projectPath}::node`,
|
|
66
122
|
path: projectPath,
|
|
67
123
|
name: pkg.name || path.basename(projectPath),
|
|
68
124
|
type: 'Node.js',
|
|
69
|
-
icon:
|
|
125
|
+
icon: projectType.icon,
|
|
70
126
|
priority: this.priority,
|
|
71
127
|
commands,
|
|
72
128
|
metadata,
|
|
73
129
|
manifest: path.basename(manifest),
|
|
74
|
-
description: pkg.description ||
|
|
130
|
+
description: pkg.description || projectType.type,
|
|
75
131
|
missingBinaries,
|
|
132
|
+
frameworks: [{ name: projectType.type, icon: projectType.icon }],
|
|
76
133
|
extra: {
|
|
77
134
|
scripts: Object.keys(scripts),
|
|
78
|
-
setupHints
|
|
135
|
+
setupHints,
|
|
136
|
+
workspaces
|
|
79
137
|
}
|
|
80
138
|
};
|
|
81
139
|
}
|
package/src/detectors/php.js
CHANGED
|
@@ -1,13 +1,106 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import path from 'path';
|
|
2
|
-
import { checkBinary } from './utils.js';
|
|
3
|
+
import { checkBinary, hasProjectFile } from './utils.js';
|
|
4
|
+
|
|
5
|
+
function parseComposerJson(content) {
|
|
6
|
+
try {
|
|
7
|
+
const pkg = JSON.parse(content);
|
|
8
|
+
return {
|
|
9
|
+
name: pkg.name || '',
|
|
10
|
+
description: pkg.description || '',
|
|
11
|
+
dependencies: [
|
|
12
|
+
...Object.keys(pkg.require || {}),
|
|
13
|
+
...Object.keys(pkg['require-dev'] || {})
|
|
14
|
+
],
|
|
15
|
+
scripts: pkg.scripts || {}
|
|
16
|
+
};
|
|
17
|
+
} catch {
|
|
18
|
+
return { name: '', description: '', dependencies: [], scripts: {} };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function detectPhpFrameworks(deps) {
|
|
23
|
+
const frameworks = [];
|
|
24
|
+
const depStr = deps.join(' ').toLowerCase();
|
|
25
|
+
|
|
26
|
+
if (depStr.includes('laravel/framework')) frameworks.push({ name: 'Laravel', icon: '๐งก' });
|
|
27
|
+
if (depStr.includes('symfony/symfony') || depStr.includes('symfony/framework-bundle')) frameworks.push({ name: 'Symfony', icon: '๐ต' });
|
|
28
|
+
if (depStr.includes('codeigniter4/framework')) frameworks.push({ name: 'CodeIgniter', icon: '๐ฅ' });
|
|
29
|
+
if (depStr.includes('cakephp/cakephp')) frameworks.push({ name: 'CakePHP', icon: '๐ฐ' });
|
|
30
|
+
if (depStr.includes('slim/slim')) frameworks.push({ name: 'Slim', icon: '๐' });
|
|
31
|
+
if (depStr.includes('lumen')) frameworks.push({ name: 'Lumen', icon: '๐ก' });
|
|
32
|
+
if (depStr.includes('phpunit/phpunit')) frameworks.push({ name: 'PHPUnit', icon: 'โ
' });
|
|
33
|
+
if (depStr.includes('laravel/octane')) frameworks.push({ name: 'Laravel Octane', icon: '๐' });
|
|
34
|
+
return frameworks;
|
|
35
|
+
}
|
|
36
|
+
|
|
3
37
|
export default {
|
|
4
|
-
type: 'php',
|
|
38
|
+
type: 'php',
|
|
39
|
+
label: 'PHP',
|
|
40
|
+
icon: '๐',
|
|
41
|
+
priority: 65,
|
|
42
|
+
files: ['composer.json'],
|
|
43
|
+
binaries: ['php', 'composer'],
|
|
5
44
|
async build(projectPath, manifest) {
|
|
6
45
|
const missingBinaries = this.binaries.filter(b => !checkBinary(b));
|
|
46
|
+
let metadata = { name: '', description: '', dependencies: [], scripts: {} };
|
|
47
|
+
let frameworks = [];
|
|
48
|
+
|
|
49
|
+
const composerPath = path.join(projectPath, 'composer.json');
|
|
50
|
+
if (fs.existsSync(composerPath)) {
|
|
51
|
+
const content = fs.readFileSync(composerPath, 'utf-8');
|
|
52
|
+
metadata = parseComposerJson(content);
|
|
53
|
+
frameworks = detectPhpFrameworks(metadata.dependencies);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const commands = {
|
|
57
|
+
install: { label: 'Composer install', command: ['composer', 'install'], source: 'builtin' },
|
|
58
|
+
update: { label: 'Composer update', command: ['composer', 'update'], source: 'builtin' }
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (hasProjectFile(projectPath, 'artisan')) {
|
|
62
|
+
commands.run = { label: 'Artisan serve', command: ['php', 'artisan', 'serve'], source: 'builtin' };
|
|
63
|
+
commands.test = { label: 'Artisan test', command: ['php', 'artisan', 'test'], source: 'builtin' };
|
|
64
|
+
commands.migrate = { label: 'Artisan migrate', command: ['php', 'artisan', 'migrate'], source: 'builtin' };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (hasProjectFile(projectPath, 'bin/phpunit') || metadata.dependencies.includes('phpunit/phpunit')) {
|
|
68
|
+
commands.test = { label: 'PHPUnit', command: ['php', 'bin/phpunit'], source: 'builtin' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (hasProjectFile(projectPath, 'symfony.lock')) {
|
|
72
|
+
commands.run = { label: 'Symfony server', command: ['symfony', 'server:start'], source: 'builtin' };
|
|
73
|
+
commands.test = { label: 'Symfony test', command: ['php', 'bin/phpunit'], source: 'builtin' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const setupHints = [];
|
|
77
|
+
if (missingBinaries.includes('composer')) {
|
|
78
|
+
setupHints.push('Install Composer: https://getcomposer.org/');
|
|
79
|
+
}
|
|
80
|
+
if (metadata.dependencies.length > 0) {
|
|
81
|
+
setupHints.push('Run composer install to fetch dependencies');
|
|
82
|
+
}
|
|
83
|
+
|
|
7
84
|
return {
|
|
8
|
-
id: `${projectPath}::php`,
|
|
9
|
-
|
|
10
|
-
|
|
85
|
+
id: `${projectPath}::php`,
|
|
86
|
+
path: projectPath,
|
|
87
|
+
name: metadata.name || path.basename(projectPath),
|
|
88
|
+
type: 'PHP',
|
|
89
|
+
icon: '๐',
|
|
90
|
+
priority: this.priority,
|
|
91
|
+
commands,
|
|
92
|
+
metadata: {
|
|
93
|
+
...metadata,
|
|
94
|
+
packageManager: 'composer'
|
|
95
|
+
},
|
|
96
|
+
manifest: path.basename(manifest),
|
|
97
|
+
description: metadata.description || frameworks.map(f => f.name).join(', '),
|
|
98
|
+
missingBinaries,
|
|
99
|
+
frameworks,
|
|
100
|
+
extra: {
|
|
101
|
+
setupHints,
|
|
102
|
+
scripts: Object.keys(metadata.scripts)
|
|
103
|
+
}
|
|
11
104
|
};
|
|
12
105
|
}
|
|
13
106
|
};
|