@produtype/core 0.49.0 → 0.51.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/README.md +1 -1
- package/dist/analyzer/analyzeProject.js +61 -0
- package/dist/analyzer/catalogue.d.ts +27 -0
- package/dist/analyzer/catalogue.js +77 -1
- package/dist/analyzer/detectBackend.js +61 -1
- package/dist/analyzer/detectContext.d.ts +3 -0
- package/dist/analyzer/detectContext.js +4 -0
- package/dist/analyzer/detectDatabase.js +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ prodkit plan ../my-app --output prodkit-plan.md
|
|
|
174
174
|
|
|
175
175
|
_Generated from the analyzer itself — run `npm run docs:stacks` after changing a detector._
|
|
176
176
|
|
|
177
|
-
- **Backend:** Express, Next.js, NestJS, Fastify, Hono, Elysia, Koa, AdonisJS, SvelteKit, Remix, Nuxt, Nitro, Astro, Django, Flask, FastAPI, aiohttp, Litestar, Sanic, Tornado, Starlette, Streamlit, Gradio, Dash, Chainlit, Gin, Echo, Fiber, chi, Gorilla, Beego, Go, Rails, Sinatra, Hanami, Roda, Grape, Ruby, Laravel, Symfony, Slim, CodeIgniter, CakePHP, Yii, PHP, ASP.NET Core, .NET
|
|
177
|
+
- **Backend:** Express, Next.js, NestJS, Fastify, Hono, Elysia, Koa, AdonisJS, SvelteKit, Remix, Nuxt, Nitro, Astro, Django, Flask, FastAPI, aiohttp, Litestar, Sanic, Tornado, Starlette, Streamlit, Gradio, Dash, Chainlit, Gin, Echo, Fiber, chi, Gorilla, Beego, Go, Axum, Actix Web, Rocket, Warp, Tide, Poem, Salvo, Tower HTTP, Spring Boot, Quarkus, Micronaut, Ktor, Javalin, Vert.x, Dropwizard, Helidon, Rails, Sinatra, Hanami, Roda, Grape, Ruby, Laravel, Symfony, Slim, CodeIgniter, CakePHP, Yii, PHP, ASP.NET Core, .NET
|
|
178
178
|
- **Frontend:** React, Vite, Vue, Nuxt, Svelte, Angular, Astro, Solid, Qwik, Preact, Remix, htmx, Tailwind CSS, Electron
|
|
179
179
|
- **Mobile:** Flutter, React Native, iOS (native), Android (native)
|
|
180
180
|
- **Databases:** Postgres, MySQL, SQLite, SQL Server, MongoDB, Redis, Firestore, DynamoDB, Convex
|
|
@@ -435,6 +435,43 @@ async function analyzeProject(projectPath) {
|
|
|
435
435
|
goDeps.push(match[1].toLowerCase());
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Cargo.toml, read for the crates a Rust project depends on.
|
|
440
|
+
*
|
|
441
|
+
* windmill's backend is 547 Rust files and the report said "Backend: go", on the
|
|
442
|
+
* strength of one `go.mod` belonging to a client SDK. Rust was not being read at
|
|
443
|
+
* all — a whole ecosystem invisible, so a Rust web service could only ever be
|
|
444
|
+
* reported as whatever else happened to be lying around.
|
|
445
|
+
*
|
|
446
|
+
* The same hand-written reader as the others: `[dependencies]` and its variants open
|
|
447
|
+
* a block, and each entry is a crate name before `=`. A version table written as
|
|
448
|
+
* `[dependencies.axum]` names the crate in the heading instead, so both shapes are
|
|
449
|
+
* read.
|
|
450
|
+
*/
|
|
451
|
+
const rustDeps = [];
|
|
452
|
+
for (const file of allFiles.filter((f) => /(^|\/)Cargo\.toml$/.test(f))) {
|
|
453
|
+
const raw = (await (0, readTextFileSafe_1.readTextFileSafe)(root, file)) ?? '';
|
|
454
|
+
let inDeps = false;
|
|
455
|
+
for (const line of raw.split('\n')) {
|
|
456
|
+
const heading = /^\s*\[([^\]]+)\]/.exec(line);
|
|
457
|
+
if (heading) {
|
|
458
|
+
const section = heading[1].trim();
|
|
459
|
+
const nested = /^(?:[a-z-]+\.)?(?:dependencies|dev-dependencies|build-dependencies)\.(.+)$/.exec(section);
|
|
460
|
+
if (nested) {
|
|
461
|
+
rustDeps.push(nested[1].trim().toLowerCase());
|
|
462
|
+
inDeps = false;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
inDeps = /(^|\.)(dependencies|dev-dependencies|build-dependencies)$/.test(section);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (!inDeps)
|
|
469
|
+
continue;
|
|
470
|
+
const entry = /^\s*([A-Za-z0-9_-]+)\s*=/.exec(line);
|
|
471
|
+
if (entry)
|
|
472
|
+
rustDeps.push(entry[1].toLowerCase());
|
|
473
|
+
}
|
|
474
|
+
}
|
|
438
475
|
/**
|
|
439
476
|
* pubspec.yaml, read for its two dependency blocks.
|
|
440
477
|
*
|
|
@@ -488,7 +525,30 @@ async function analyzeProject(projectPath) {
|
|
|
488
525
|
* reported, which is the direction to err in — the catalog is the project's own
|
|
489
526
|
* statement about what it builds with.
|
|
490
527
|
*/
|
|
528
|
+
/**
|
|
529
|
+
* pom.xml, read for the coordinates a Maven project declares.
|
|
530
|
+
*
|
|
531
|
+
* A Spring Boot REST API with Spring Security and PostgreSQL reported no backend and
|
|
532
|
+
* no database. The manifest was recognised well enough to name the package manager
|
|
533
|
+
* "maven" and then never opened — the same shape of gap Rust had, in the larger
|
|
534
|
+
* ecosystem.
|
|
535
|
+
*
|
|
536
|
+
* `<parent>` is read alongside `<dependencies>` because that is where Spring Boot
|
|
537
|
+
* declares itself: `spring-boot-starter-parent` is the single line that makes a
|
|
538
|
+
* project a Spring Boot project, and a starter listed below it inherits its version
|
|
539
|
+
* from there rather than stating one.
|
|
540
|
+
*
|
|
541
|
+
* Regular expressions rather than an XML parser, for the reason the others give: the
|
|
542
|
+
* shape being read is two adjacent elements, and a manifest this cannot read yields
|
|
543
|
+
* no dependencies rather than a wrong answer.
|
|
544
|
+
*/
|
|
491
545
|
const gradleDeps = [];
|
|
546
|
+
for (const file of allFiles.filter((f) => /(^|\/)pom\.xml$/.test(f))) {
|
|
547
|
+
const raw = (await (0, readTextFileSafe_1.readTextFileSafe)(root, file)) ?? '';
|
|
548
|
+
for (const match of raw.matchAll(/<groupId>\s*([^<\s]+)\s*<\/groupId>\s*<artifactId>\s*([^<\s]+)\s*<\/artifactId>/g)) {
|
|
549
|
+
gradleDeps.push(`${match[1]}:${match[2]}`.toLowerCase());
|
|
550
|
+
}
|
|
551
|
+
}
|
|
492
552
|
for (const file of allFiles.filter((f) => /(^|\/)build\.gradle(\.kts)?$/.test(f))) {
|
|
493
553
|
const raw = (await (0, readTextFileSafe_1.readTextFileSafe)(root, file)) ?? '';
|
|
494
554
|
for (const match of raw.matchAll(/\b(?:implementation|api|compileOnly|runtimeOnly|kapt|ksp|annotationProcessor|testImplementation)\s*[( ]\s*["']([^"']+)["']/g)) {
|
|
@@ -664,6 +724,7 @@ async function analyzeProject(projectPath) {
|
|
|
664
724
|
pythonDeps,
|
|
665
725
|
phpDeps: unique(phpDeps),
|
|
666
726
|
goDeps: unique(goDeps),
|
|
727
|
+
rustDeps: unique(rustDeps),
|
|
667
728
|
rubyDeps: unique(rubyDeps),
|
|
668
729
|
dotnetDeps: unique(dotnetDeps),
|
|
669
730
|
dotnetWebSdk,
|
|
@@ -17,6 +17,33 @@
|
|
|
17
17
|
export declare const NODE_BACKEND_FRAMEWORKS: Array<[string, string]>;
|
|
18
18
|
/** Go frameworks, read from go.mod. */
|
|
19
19
|
export declare const GO_BACKEND_FRAMEWORKS: Array<[string, string[]]>;
|
|
20
|
+
/**
|
|
21
|
+
* Rust frameworks, read from Cargo.toml.
|
|
22
|
+
*
|
|
23
|
+
* windmill serves its requests from 547 Rust files and was reported as a Go backend,
|
|
24
|
+
* because a client SDK in the same repository carries a go.mod and Rust was not read
|
|
25
|
+
* at all.
|
|
26
|
+
*/
|
|
27
|
+
export declare const RUST_BACKEND_FRAMEWORKS: Array<[string, string[]]>;
|
|
28
|
+
/**
|
|
29
|
+
* JVM frameworks, read from pom.xml and build.gradle alike.
|
|
30
|
+
*
|
|
31
|
+
* A Spring Boot REST API with Spring Security and PostgreSQL reported no backend at
|
|
32
|
+
* all: the coordinates were never read, and no JVM server framework was ever named.
|
|
33
|
+
* Coordinates are `group:artifact` in both build systems, so one table serves both.
|
|
34
|
+
*
|
|
35
|
+
* `spring-boot-starter-parent` earns its place beside the starters: it is how a Maven
|
|
36
|
+
* project declares itself a Spring Boot project, and the starters under it inherit
|
|
37
|
+
* their version from it rather than stating one.
|
|
38
|
+
*/
|
|
39
|
+
export declare const JVM_BACKEND_FRAMEWORKS: Array<[string, string[]]>;
|
|
40
|
+
/**
|
|
41
|
+
* Databases a JVM project declares by driver.
|
|
42
|
+
*
|
|
43
|
+
* The same manifest that names the framework names the database, and a Spring Boot
|
|
44
|
+
* project with `org.postgresql:postgresql` was reported as having no data layer.
|
|
45
|
+
*/
|
|
46
|
+
export declare const JVM_DATABASES: Array<[string, string[]]>;
|
|
20
47
|
/** Ruby frameworks, read from the Gemfile. */
|
|
21
48
|
export declare const RUBY_BACKEND_FRAMEWORKS: Array<[string, string[]]>;
|
|
22
49
|
/** PHP frameworks, read from composer.json. */
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* that loses users.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.LANGUAGES = exports.FRONTEND_FRAMEWORKS = exports.PYTHON_BACKEND_FRAMEWORKS = exports.PHP_BACKEND_FRAMEWORKS = exports.RUBY_BACKEND_FRAMEWORKS = exports.GO_BACKEND_FRAMEWORKS = exports.NODE_BACKEND_FRAMEWORKS = void 0;
|
|
18
|
+
exports.LANGUAGES = exports.FRONTEND_FRAMEWORKS = exports.PYTHON_BACKEND_FRAMEWORKS = exports.PHP_BACKEND_FRAMEWORKS = exports.RUBY_BACKEND_FRAMEWORKS = exports.JVM_DATABASES = exports.JVM_BACKEND_FRAMEWORKS = exports.RUST_BACKEND_FRAMEWORKS = exports.GO_BACKEND_FRAMEWORKS = exports.NODE_BACKEND_FRAMEWORKS = void 0;
|
|
19
19
|
exports.labelFor = labelFor;
|
|
20
20
|
exports.hasLabel = hasLabel;
|
|
21
21
|
exports.supportedStacks = supportedStacks;
|
|
@@ -43,6 +43,63 @@ exports.GO_BACKEND_FRAMEWORKS = [
|
|
|
43
43
|
['gorilla', ['gorilla/mux']],
|
|
44
44
|
['beego', ['beego/beego']],
|
|
45
45
|
];
|
|
46
|
+
/**
|
|
47
|
+
* Rust frameworks, read from Cargo.toml.
|
|
48
|
+
*
|
|
49
|
+
* windmill serves its requests from 547 Rust files and was reported as a Go backend,
|
|
50
|
+
* because a client SDK in the same repository carries a go.mod and Rust was not read
|
|
51
|
+
* at all.
|
|
52
|
+
*/
|
|
53
|
+
exports.RUST_BACKEND_FRAMEWORKS = [
|
|
54
|
+
['axum', ['axum']],
|
|
55
|
+
['actix-web', ['actix-web']],
|
|
56
|
+
['rocket', ['rocket']],
|
|
57
|
+
['warp', ['warp']],
|
|
58
|
+
['tide', ['tide']],
|
|
59
|
+
['poem', ['poem']],
|
|
60
|
+
['salvo', ['salvo']],
|
|
61
|
+
['tower-http', ['tower-http']],
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* JVM frameworks, read from pom.xml and build.gradle alike.
|
|
65
|
+
*
|
|
66
|
+
* A Spring Boot REST API with Spring Security and PostgreSQL reported no backend at
|
|
67
|
+
* all: the coordinates were never read, and no JVM server framework was ever named.
|
|
68
|
+
* Coordinates are `group:artifact` in both build systems, so one table serves both.
|
|
69
|
+
*
|
|
70
|
+
* `spring-boot-starter-parent` earns its place beside the starters: it is how a Maven
|
|
71
|
+
* project declares itself a Spring Boot project, and the starters under it inherit
|
|
72
|
+
* their version from it rather than stating one.
|
|
73
|
+
*/
|
|
74
|
+
exports.JVM_BACKEND_FRAMEWORKS = [
|
|
75
|
+
['spring-boot', [
|
|
76
|
+
'org.springframework.boot:spring-boot-starter-web',
|
|
77
|
+
'org.springframework.boot:spring-boot-starter-webflux',
|
|
78
|
+
'org.springframework.boot:spring-boot-starter-parent',
|
|
79
|
+
'org.springframework.boot:spring-boot-starter',
|
|
80
|
+
]],
|
|
81
|
+
['quarkus', ['io.quarkus:quarkus-resteasy', 'io.quarkus:quarkus-resteasy-reactive', 'io.quarkus:quarkus-bom']],
|
|
82
|
+
['micronaut', ['io.micronaut:micronaut-http-server-netty', 'io.micronaut:micronaut-inject']],
|
|
83
|
+
['ktor', ['io.ktor:ktor-server-core', 'io.ktor:ktor-server-netty', 'io.ktor:ktor-server-cio']],
|
|
84
|
+
['javalin', ['io.javalin:javalin']],
|
|
85
|
+
['vertx', ['io.vertx:vertx-web', 'io.vertx:vertx-core']],
|
|
86
|
+
['dropwizard', ['io.dropwizard:dropwizard-core']],
|
|
87
|
+
['helidon', ['io.helidon.webserver:helidon-webserver']],
|
|
88
|
+
];
|
|
89
|
+
/**
|
|
90
|
+
* Databases a JVM project declares by driver.
|
|
91
|
+
*
|
|
92
|
+
* The same manifest that names the framework names the database, and a Spring Boot
|
|
93
|
+
* project with `org.postgresql:postgresql` was reported as having no data layer.
|
|
94
|
+
*/
|
|
95
|
+
exports.JVM_DATABASES = [
|
|
96
|
+
['postgres', ['org.postgresql:postgresql', 'io.r2dbc:r2dbc-postgresql']],
|
|
97
|
+
['mysql', ['mysql:mysql-connector-java', 'com.mysql:mysql-connector-j']],
|
|
98
|
+
['mariadb', ['org.mariadb.jdbc:mariadb-java-client']],
|
|
99
|
+
['sqlite', ['org.xerial:sqlite-jdbc']],
|
|
100
|
+
['mongodb', ['org.mongodb:mongodb-driver-sync', 'org.mongodb:mongo-java-driver']],
|
|
101
|
+
['redis', ['redis.clients:jedis', 'io.lettuce:lettuce-core']],
|
|
102
|
+
];
|
|
46
103
|
/** Ruby frameworks, read from the Gemfile. */
|
|
47
104
|
exports.RUBY_BACKEND_FRAMEWORKS = [
|
|
48
105
|
['rails', ['rails']],
|
|
@@ -138,6 +195,23 @@ const LABELS = {
|
|
|
138
195
|
chi: 'chi',
|
|
139
196
|
gorilla: 'Gorilla',
|
|
140
197
|
beego: 'Beego',
|
|
198
|
+
'spring-boot': 'Spring Boot',
|
|
199
|
+
quarkus: 'Quarkus',
|
|
200
|
+
micronaut: 'Micronaut',
|
|
201
|
+
ktor: 'Ktor',
|
|
202
|
+
javalin: 'Javalin',
|
|
203
|
+
vertx: 'Vert.x',
|
|
204
|
+
dropwizard: 'Dropwizard',
|
|
205
|
+
helidon: 'Helidon',
|
|
206
|
+
axum: 'Axum',
|
|
207
|
+
'actix-web': 'Actix Web',
|
|
208
|
+
rocket: 'Rocket',
|
|
209
|
+
warp: 'Warp',
|
|
210
|
+
tide: 'Tide',
|
|
211
|
+
poem: 'Poem',
|
|
212
|
+
salvo: 'Salvo',
|
|
213
|
+
'tower-http': 'Tower HTTP',
|
|
214
|
+
rust: 'Rust',
|
|
141
215
|
rails: 'Rails',
|
|
142
216
|
sinatra: 'Sinatra',
|
|
143
217
|
hanami: 'Hanami',
|
|
@@ -260,6 +334,8 @@ function supportedStacks() {
|
|
|
260
334
|
...entries(exports.PYTHON_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
261
335
|
...entries(exports.GO_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
262
336
|
{ id: 'go', label: 'Go', detectedFrom: 'a go.mod with no framework in it — net/http is a real answer' },
|
|
337
|
+
...entries(exports.RUST_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
338
|
+
...entries(exports.JVM_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
263
339
|
...entries(exports.RUBY_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
264
340
|
{ id: 'ruby', label: 'Ruby', detectedFrom: 'a Gemfile with no web framework in it' },
|
|
265
341
|
...entries(exports.PHP_BACKEND_FRAMEWORKS.map(([id]) => id)),
|
|
@@ -5,6 +5,30 @@ const detectContext_1 = require("./detectContext");
|
|
|
5
5
|
const textSearch_1 = require("../utils/textSearch");
|
|
6
6
|
const readTextFileSafe_1 = require("../utils/readTextFileSafe");
|
|
7
7
|
const catalogue_1 = require("./catalogue");
|
|
8
|
+
/**
|
|
9
|
+
* How much of the source is written in one language.
|
|
10
|
+
*
|
|
11
|
+
* A manifest says a language is present; a share says it is what the product is made
|
|
12
|
+
* of. The mobile detector has drawn this distinction since a single MAUI client
|
|
13
|
+
* decided the profile of a nine-project .NET solution.
|
|
14
|
+
*/
|
|
15
|
+
function languageShare(ctx, extension) {
|
|
16
|
+
const source = ctx.files.source;
|
|
17
|
+
if (source.length === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
return source.filter((file) => extension.test(file)).length / source.length;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Below this a language is present in the repository without being what serves the
|
|
23
|
+
* requests.
|
|
24
|
+
*
|
|
25
|
+
* windmill is the measurement: two Go files beside 547 Rust ones, 0.05% of its source,
|
|
26
|
+
* and the report said "Backend: go". The line is not tuned to that case — anything
|
|
27
|
+
* under one file in twenty is a client, a script or a sample, and every backend in the
|
|
28
|
+
* verification corpus is far above it. A repository genuinely split between two server
|
|
29
|
+
* languages reports both, which is the right answer for one.
|
|
30
|
+
*/
|
|
31
|
+
const MINIMUM_BACKEND_SHARE = 0.05;
|
|
8
32
|
async function detectBackend(ctx) {
|
|
9
33
|
const frameworks = [];
|
|
10
34
|
const evidence = [];
|
|
@@ -63,10 +87,46 @@ async function detectBackend(ctx) {
|
|
|
63
87
|
for (const dep of hits)
|
|
64
88
|
evidence.push({ type: 'dependency', value: dep });
|
|
65
89
|
}
|
|
66
|
-
|
|
90
|
+
/**
|
|
91
|
+
* A go.mod is not a Go backend on its own.
|
|
92
|
+
*
|
|
93
|
+
* windmill carries one for a client SDK beside 547 Rust files, and the report said
|
|
94
|
+
* "Backend: go". The same share test the mobile detector already uses: a language
|
|
95
|
+
* has to be a real part of what is written here before it names the backend.
|
|
96
|
+
*/
|
|
97
|
+
const goShare = languageShare(ctx, /\.go$/);
|
|
98
|
+
if (!namedGoFramework && goShare >= MINIMUM_BACKEND_SHARE && ctx.files.all.some((f) => /(^|\/)go\.mod$/.test(f))) {
|
|
67
99
|
frameworks.push('go');
|
|
68
100
|
evidence.push({ type: 'note', value: 'a Go module with no web framework named in go.mod' });
|
|
69
101
|
}
|
|
102
|
+
/** The JVM, read from pom.xml and build.gradle alike: coordinates are the same shape. */
|
|
103
|
+
for (const [framework, deps] of catalogue_1.JVM_BACKEND_FRAMEWORKS) {
|
|
104
|
+
const hits = (0, detectContext_1.hasAnyGradleDep)(ctx, deps);
|
|
105
|
+
if (!hits.length)
|
|
106
|
+
continue;
|
|
107
|
+
frameworks.push(framework);
|
|
108
|
+
for (const dep of hits)
|
|
109
|
+
evidence.push({ type: 'dependency', value: dep });
|
|
110
|
+
}
|
|
111
|
+
/** Rust, read from Cargo.toml. */
|
|
112
|
+
let namedRustFramework = false;
|
|
113
|
+
for (const [framework, deps] of catalogue_1.RUST_BACKEND_FRAMEWORKS) {
|
|
114
|
+
const hits = (0, detectContext_1.hasAnyRustDep)(ctx, deps);
|
|
115
|
+
if (!hits.length)
|
|
116
|
+
continue;
|
|
117
|
+
namedRustFramework = true;
|
|
118
|
+
frameworks.push(framework);
|
|
119
|
+
for (const dep of hits)
|
|
120
|
+
evidence.push({ type: 'dependency', value: dep });
|
|
121
|
+
}
|
|
122
|
+
// Same reasoning as Go: a crate that serves requests from the standard library and a
|
|
123
|
+
// hand-rolled loop is still a backend, and a Cargo.toml alone is not.
|
|
124
|
+
if (!namedRustFramework
|
|
125
|
+
&& languageShare(ctx, /\.rs$/) >= MINIMUM_BACKEND_SHARE
|
|
126
|
+
&& ctx.files.all.some((f) => /(^|\/)Cargo\.toml$/.test(f))) {
|
|
127
|
+
frameworks.push('rust');
|
|
128
|
+
evidence.push({ type: 'note', value: 'a Cargo manifest with no web framework named in it' });
|
|
129
|
+
}
|
|
70
130
|
/** Ruby, read from the Gemfile. */
|
|
71
131
|
let namedRubyFramework = false;
|
|
72
132
|
for (const [framework, deps] of catalogue_1.RUBY_BACKEND_FRAMEWORKS) {
|
|
@@ -26,6 +26,8 @@ export interface DetectContext {
|
|
|
26
26
|
phpDeps: string[];
|
|
27
27
|
/** Module paths from go.mod, lowercase. */
|
|
28
28
|
goDeps: string[];
|
|
29
|
+
/** Crate names from Cargo.toml, lowercase. */
|
|
30
|
+
rustDeps: string[];
|
|
29
31
|
/** Gem names from the Gemfile, lowercase. */
|
|
30
32
|
rubyDeps: string[];
|
|
31
33
|
/** PackageReference and FrameworkReference names from .csproj, lowercase. */
|
|
@@ -71,6 +73,7 @@ export declare function hasRuntimeDep(ctx: DetectContext, name: string): boolean
|
|
|
71
73
|
export declare function hasRuntimePyDep(ctx: DetectContext, name: string): boolean;
|
|
72
74
|
export declare function hasAnyDep(ctx: DetectContext, names: string[]): string[];
|
|
73
75
|
export declare function hasPyDep(ctx: DetectContext, name: string): boolean;
|
|
76
|
+
export declare function hasAnyRustDep(ctx: DetectContext, names: string[]): string[];
|
|
74
77
|
export declare function hasAnyPyDep(ctx: DetectContext, names: string[]): string[];
|
|
75
78
|
export declare function hasPhpDep(ctx: DetectContext, name: string): boolean;
|
|
76
79
|
export declare function hasAnyPhpDep(ctx: DetectContext, names: string[]): string[];
|
|
@@ -5,6 +5,7 @@ exports.hasRuntimeDep = hasRuntimeDep;
|
|
|
5
5
|
exports.hasRuntimePyDep = hasRuntimePyDep;
|
|
6
6
|
exports.hasAnyDep = hasAnyDep;
|
|
7
7
|
exports.hasPyDep = hasPyDep;
|
|
8
|
+
exports.hasAnyRustDep = hasAnyRustDep;
|
|
8
9
|
exports.hasAnyPyDep = hasAnyPyDep;
|
|
9
10
|
exports.hasPhpDep = hasPhpDep;
|
|
10
11
|
exports.hasAnyPhpDep = hasAnyPhpDep;
|
|
@@ -36,6 +37,9 @@ function hasAnyDep(ctx, names) {
|
|
|
36
37
|
function hasPyDep(ctx, name) {
|
|
37
38
|
return ctx.pythonDeps.includes(name.toLowerCase());
|
|
38
39
|
}
|
|
40
|
+
function hasAnyRustDep(ctx, names) {
|
|
41
|
+
return names.filter((name) => ctx.rustDeps.includes(name.toLowerCase()));
|
|
42
|
+
}
|
|
39
43
|
function hasAnyPyDep(ctx, names) {
|
|
40
44
|
return names.filter((n) => hasPyDep(ctx, n));
|
|
41
45
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.detectDatabase = detectDatabase;
|
|
4
4
|
const detectContext_1 = require("./detectContext");
|
|
5
5
|
const readTextFileSafe_1 = require("../utils/readTextFileSafe");
|
|
6
|
+
const catalogue_1 = require("./catalogue");
|
|
6
7
|
/**
|
|
7
8
|
* Managed data platforms, and the engine each one actually is.
|
|
8
9
|
*
|
|
@@ -94,6 +95,20 @@ async function detectDatabase(ctx) {
|
|
|
94
95
|
evidence.push({ type: 'dependency', value: h });
|
|
95
96
|
}
|
|
96
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The JVM, by driver coordinate.
|
|
100
|
+
*
|
|
101
|
+
* The same manifest that names Spring Boot names PostgreSQL, and a Spring Boot API
|
|
102
|
+
* with `org.postgresql:postgresql` in its pom was reported as having no data layer.
|
|
103
|
+
*/
|
|
104
|
+
for (const [db, names] of catalogue_1.JVM_DATABASES) {
|
|
105
|
+
const hits = (0, detectContext_1.hasAnyGradleDep)(ctx, names);
|
|
106
|
+
if (hits.length) {
|
|
107
|
+
databases.add(db);
|
|
108
|
+
for (const h of hits)
|
|
109
|
+
evidence.push({ type: 'dependency', value: h });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
const goHits = [
|
|
98
113
|
['postgres', ['jackc/pgx', 'jackc/pgx/v5', 'lib/pq']],
|
|
99
114
|
['mysql', ['go-sql-driver/mysql']],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|