Skip to content

Commit 1c2bdd1

Browse files
feat: detect standalone React projects
Co-authored-by: devmap-agent <238585242+devmap-agent@users.noreply.github.com>
1 parent 365ac1b commit 1c2bdd1

15 files changed

Lines changed: 164 additions & 18 deletions

File tree

PRD.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ Run static analysis, generate project snapshot, and output a readable project ov
306306

307307
- Next.js
308308
- Express
309+
- React
309310

310311
**Responsibilities:**
311312

@@ -1330,7 +1331,7 @@ Landing page/web can be added later under `apps/web`, but must not distract from
13301331

13311332
| Risk | Mitigation |
13321333
|---|---|
1333-
| Static analysis inaccurate on unconventional projects | Limit MVP to Next.js + Express only |
1334+
| Static analysis inaccurate on unconventional projects | Keep MVP detection scoped to tested Next.js, Express, and standalone React signals |
13341335
| User frustrated by API key setup | `devmap init` wizard is fully guided |
13351336
| AI output misleading | Frame output as overview, not absolute ground truth |
13361337
| Windows / Mac / Linux inconsistency | Test with GitHub Actions matrix |

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,10 @@ Use AI coding assistants to modify it.
246246

247247
* Next.js
248248
* Express
249+
* React
249250

250251
### Planned
251252

252-
* React
253253
* NestJS
254254
* Laravel
255255
* Nuxt

docs/architecture.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ Terminal Output
6161

6262
DevMap MVP focuses on:
6363

64-
* Next.js
65-
* Express
64+
* Next.js
65+
* Express
66+
* React
6667

6768
Other stacks are future roadmap items.
6869

@@ -162,10 +163,16 @@ Framework detection should use:
162163
| Signal | Detection |
163164
| -------------------------- | ------------------------ |
164165
| `next` dependency | Next.js |
165-
| `app/` directory | Next.js App Router |
166-
| `pages/` directory | Next.js Pages Router |
167-
| `express` dependency | Express |
168-
| `server.ts` or `server.js` | Node/Express entry point |
166+
| `app/page`, `app/layout`, or `app/route` | Next.js App Router |
167+
| `pages/_app`, `pages/_document`, or `pages/api` | Next.js Pages Router |
168+
| `express` dependency | Express |
169+
| `server.ts` or `server.js` | Node/Express entry point |
170+
| `react` plus browser runtime/tooling and JSX/TSX source | Standalone React |
171+
172+
Next.js detection runs before React because Next projects also depend on
173+
React. A generic `src/app/` folder is not enough to infer Next.js; source-only
174+
fallback requires Next conventions such as `app/page`, `app/layout`,
175+
`app/route`, or a Next config file.
169176

170177
---
171178

@@ -445,7 +452,7 @@ its `sourcePriority` files before broad repository exploration. The full
445452
The index separates technical framework detection from repository shape:
446453

447454
```txt
448-
framework -> nextjs | express | unknown
455+
framework -> nextjs | react | express | unknown
449456
projectType -> node-cli | web-app | api-service | library | unknown
450457
workspaceType -> monorepo | single-package
451458
```

docs/for-me-personal/PROGRESS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ Terakhir diperbarui: 2026-06-20
44

55
## Update 2026-06-20
66

7+
### Standalone React Detection
8+
9+
- Framework detector sekarang mengenali standalone React dari dependency
10+
`react`, browser runtime/tooling, dan bukti JSX/TSX source.
11+
- Next.js tetap memiliki precedence karena Next juga memakai React.
12+
- Folder generik `src/app/` tidak lagi otomatis dianggap Next.js; fallback
13+
source membutuhkan `app/page`, `app/layout`, `app/route`, atau Next config.
14+
- React peer dependency tanpa runtime app tidak diklasifikasikan sebagai
15+
framework React, sehingga component library tidak menjadi false positive.
16+
- Entry detector sekarang mengenali `main.tsx` sebagai browser entry point.
17+
- Packed-package E2E mencakup fixture React selain Next.js dan Express.
18+
719
### Project Classification Dan Start-Here Ranking
820

921
- Agent index sekarang memisahkan `framework`, `projectType`, dan

docs/for-me-personal/TEST.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ Fixture aman digunakan karena tidak mengubah project pribadi:
436436
```powershell
437437
pnpm dev:cli -- analyze packages/cli/test/fixtures/nextjs-project --fresh
438438
pnpm dev:cli -- analyze packages/cli/test/fixtures/express-project --fresh
439+
pnpm dev:cli -- analyze packages/cli/test/fixtures/react-project --fresh
439440
```
440441

441442
Hasil penting Next.js:
@@ -448,6 +449,14 @@ Hasil penting Next.js:
448449
Hasil penting Express:
449450

450451
- framework `express`;
452+
453+
Expected React fixture:
454+
455+
- framework `react`;
456+
- project type `web-app`;
457+
- entry point `src/main.tsx`;
458+
- tidak menghasilkan route palsu hanya karena memakai React;
459+
- package dengan React peer dependency saja tetap `unknown`.
451460
- entry point `src/server.ts`;
452461
- route payment dan Stripe terdeteksi.
453462

docs/generated-files.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ The snapshot contains:
105105

106106
- `version` and `generatedAt`
107107
- `fingerprint` for stale snapshot detection
108-
- project name, root, framework, language, and package manager
108+
- project name, root, framework, project/workspace type, language, and package manager
109109
- file and line statistics
110110
- entry points and scored critical files with reasons
111111
- page routes and API routes

packages/cli/src/analyzers/entryPoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const ENTRY_PATTERNS = [
55
/(^|\/)page\.[jt]sx?$/,
66
/(^|\/)layout\.[jt]sx?$/,
77
/(^|\/)middleware\.[jt]s$/,
8-
/(^|\/)(server|app|index|main)\.[cm]?[jt]s$/,
8+
/(^|\/)(server|app|index|main)\.[cm]?[jt]sx?$/,
99
/(^|\/)route\.[jt]s$/
1010
];
1111

packages/cli/src/analyzers/frameworkDetector.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import type { ScannedFile } from "./fileScanner.js";
22
import { isArchitectureSource } from "./sourceScope.js";
33

4-
export type Framework = "nextjs" | "express" | "unknown";
4+
export type Framework = "nextjs" | "react" | "express" | "unknown";
55

66
export function detectFramework(files: ScannedFile[]): Framework {
7-
const packageJson = files.find((file) => file.path === "package.json");
8-
const dependencies = packageJson ? readDependencies(packageJson.content) : {};
7+
const dependencies = readAllDependencies(files);
98
const sourceFiles = files.filter((file) => isArchitectureSource(file.path));
109

1110
if (
1211
"next" in dependencies
13-
|| sourceFiles.some((file) => /^(?:src\/)?(?:app|pages)\//.test(file.path))
12+
|| files.some((file) => /^next\.config\.[cm]?[jt]s$/.test(file.path))
13+
|| sourceFiles.some((file) =>
14+
/^(?:src\/)?app\/(?:.+\/)?(?:page|layout|route)\.[jt]sx?$/.test(file.path)
15+
|| /^(?:src\/)?pages\/(?:_app|_document|api\/)/.test(file.path)
16+
)
1417
) {
1518
return "nextjs";
1619
}
@@ -22,9 +25,31 @@ export function detectFramework(files: ScannedFile[]): Framework {
2225
return "express";
2326
}
2427

28+
const hasReactRuntime = "react-dom" in dependencies
29+
|| "react-scripts" in dependencies
30+
|| "@vitejs/plugin-react" in dependencies
31+
|| "@vitejs/plugin-react-swc" in dependencies;
32+
const hasReactSource = sourceFiles.some((file) =>
33+
/\.[jt]sx$/.test(file.path)
34+
|| /(?:from\s+["']react["']|from\s+["']react-dom(?:\/client)?["']|require\(["']react["']\))/.test(file.content)
35+
);
36+
37+
if ("react" in dependencies && hasReactRuntime && hasReactSource) {
38+
return "react";
39+
}
40+
2541
return "unknown";
2642
}
2743

44+
function readAllDependencies(files: ScannedFile[]): Record<string, string> {
45+
return files
46+
.filter((file) => file.path.endsWith("package.json"))
47+
.reduce((dependencies, file) => ({
48+
...dependencies,
49+
...readDependencies(file.content)
50+
}), {} as Record<string, string>);
51+
}
52+
2853
function readDependencies(content: string): Record<string, string> {
2954
try {
3055
const parsed = JSON.parse(content) as {

packages/cli/src/analyzers/projectMetadata.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ function detectProjectType(
8585
manifests: PackageManifest[]
8686
): ProjectType {
8787
if (manifests.some((manifest) => manifest.bin)) return "node-cli";
88-
if (framework === "nextjs" || hasDependency(manifests, "astro")) return "web-app";
88+
if (["nextjs", "react"].includes(framework) || hasDependency(manifests, "astro")) {
89+
return "web-app";
90+
}
8991
if (framework === "express") return "api-service";
9092
if (manifests.some((manifest) => manifest.exports || manifest.main)) return "library";
9193
return "unknown";

packages/cli/src/cache/snapshot.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ function normalizeSnapshotDefaults(snapshot: Record<string, unknown>): void {
142142

143143
if (isRecord(snapshot.project)) {
144144
if (typeof snapshot.project.projectType !== "string") {
145-
snapshot.project.projectType = snapshot.project.framework === "nextjs"
145+
snapshot.project.projectType = ["nextjs", "react"].includes(String(snapshot.project.framework))
146146
? "web-app"
147147
: snapshot.project.framework === "express"
148148
? "api-service"

0 commit comments

Comments
 (0)