@cr8rcho/alkahest 0.1.1
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.ko.md +208 -0
- package/README.md +208 -0
- package/dist/assets/dashboard.html +647 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +68 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/hook.d.ts +1 -0
- package/dist/commands/hook.js +64 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/login.d.ts +11 -0
- package/dist/commands/login.js +27 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.js +13 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/publish.d.ts +19 -0
- package/dist/commands/publish.js +27 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/scan.d.ts +11 -0
- package/dist/commands/scan.js +29 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/update.d.ts +15 -0
- package/dist/commands/update.js +41 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +21 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/core/adapters/angular.d.ts +2 -0
- package/dist/core/adapters/angular.js +305 -0
- package/dist/core/adapters/angular.js.map +1 -0
- package/dist/core/adapters/astro.d.ts +3 -0
- package/dist/core/adapters/astro.js +140 -0
- package/dist/core/adapters/astro.js.map +1 -0
- package/dist/core/adapters/compose.d.ts +2 -0
- package/dist/core/adapters/compose.js +195 -0
- package/dist/core/adapters/compose.js.map +1 -0
- package/dist/core/adapters/django.d.ts +2 -0
- package/dist/core/adapters/django.js +314 -0
- package/dist/core/adapters/django.js.map +1 -0
- package/dist/core/adapters/expo-router.d.ts +2 -0
- package/dist/core/adapters/expo-router.js +60 -0
- package/dist/core/adapters/expo-router.js.map +1 -0
- package/dist/core/adapters/flask.d.ts +2 -0
- package/dist/core/adapters/flask.js +249 -0
- package/dist/core/adapters/flask.js.map +1 -0
- package/dist/core/adapters/flutter.d.ts +2 -0
- package/dist/core/adapters/flutter.js +232 -0
- package/dist/core/adapters/flutter.js.map +1 -0
- package/dist/core/adapters/index.d.ts +19 -0
- package/dist/core/adapters/index.js +59 -0
- package/dist/core/adapters/index.js.map +1 -0
- package/dist/core/adapters/next-app.d.ts +2 -0
- package/dist/core/adapters/next-app.js +62 -0
- package/dist/core/adapters/next-app.js.map +1 -0
- package/dist/core/adapters/next-pages.d.ts +2 -0
- package/dist/core/adapters/next-pages.js +70 -0
- package/dist/core/adapters/next-pages.js.map +1 -0
- package/dist/core/adapters/nuxt.d.ts +2 -0
- package/dist/core/adapters/nuxt.js +59 -0
- package/dist/core/adapters/nuxt.js.map +1 -0
- package/dist/core/adapters/rails.d.ts +2 -0
- package/dist/core/adapters/rails.js +275 -0
- package/dist/core/adapters/rails.js.map +1 -0
- package/dist/core/adapters/react-jsx.d.ts +68 -0
- package/dist/core/adapters/react-jsx.js +355 -0
- package/dist/core/adapters/react-jsx.js.map +1 -0
- package/dist/core/adapters/react-navigation.d.ts +2 -0
- package/dist/core/adapters/react-navigation.js +153 -0
- package/dist/core/adapters/react-navigation.js.map +1 -0
- package/dist/core/adapters/react-router.d.ts +2 -0
- package/dist/core/adapters/react-router.js +249 -0
- package/dist/core/adapters/react-router.js.map +1 -0
- package/dist/core/adapters/remix.d.ts +2 -0
- package/dist/core/adapters/remix.js +109 -0
- package/dist/core/adapters/remix.js.map +1 -0
- package/dist/core/adapters/static-html.d.ts +2 -0
- package/dist/core/adapters/static-html.js +157 -0
- package/dist/core/adapters/static-html.js.map +1 -0
- package/dist/core/adapters/sveltekit.d.ts +2 -0
- package/dist/core/adapters/sveltekit.js +151 -0
- package/dist/core/adapters/sveltekit.js.map +1 -0
- package/dist/core/adapters/swiftui.d.ts +2 -0
- package/dist/core/adapters/swiftui.js +216 -0
- package/dist/core/adapters/swiftui.js.map +1 -0
- package/dist/core/adapters/types.d.ts +65 -0
- package/dist/core/adapters/types.js +2 -0
- package/dist/core/adapters/types.js.map +1 -0
- package/dist/core/adapters/uikit.d.ts +2 -0
- package/dist/core/adapters/uikit.js +178 -0
- package/dist/core/adapters/uikit.js.map +1 -0
- package/dist/core/adapters/vue-router.d.ts +2 -0
- package/dist/core/adapters/vue-router.js +224 -0
- package/dist/core/adapters/vue-router.js.map +1 -0
- package/dist/core/adapters/vue-sfc.d.ts +28 -0
- package/dist/core/adapters/vue-sfc.js +132 -0
- package/dist/core/adapters/vue-sfc.js.map +1 -0
- package/dist/core/credentials.d.ts +24 -0
- package/dist/core/credentials.js +30 -0
- package/dist/core/credentials.js.map +1 -0
- package/dist/core/dashboard.d.ts +9 -0
- package/dist/core/dashboard.js +20 -0
- package/dist/core/dashboard.js.map +1 -0
- package/dist/core/emit.d.ts +7 -0
- package/dist/core/emit.js +23 -0
- package/dist/core/emit.js.map +1 -0
- package/dist/core/hash.d.ts +2 -0
- package/dist/core/hash.js +6 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/pipeline.d.ts +25 -0
- package/dist/core/pipeline.js +122 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/publish.d.ts +31 -0
- package/dist/core/publish.js +87 -0
- package/dist/core/publish.js.map +1 -0
- package/dist/core/resolve.d.ts +42 -0
- package/dist/core/resolve.js +128 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/serve.d.ts +5 -0
- package/dist/core/serve.js +52 -0
- package/dist/core/serve.js.map +1 -0
- package/dist/core/types.d.ts +117 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/version.d.ts +23 -0
- package/dist/core/version.js +102 -0
- package/dist/core/version.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +224 -0
- package/dist/mcp/server.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { statSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
import { sourceFileFor, walk, parseReactScreen, titleFromRoute, isReactRouterSpa, isReactNativeApp, isVueApp, isAndroidApp, isRemixApp, isAstroApp, isAngularApp, isRailsApp } from "./react-jsx.js";
|
|
4
|
+
/**
|
|
5
|
+
* Next.js app-router adapter: treats `app/**/page.tsx` as screens, parses with ts-morph.
|
|
6
|
+
* Screen id = route ("/dashboard/settings"). Entry point = root route "/".
|
|
7
|
+
* JSX signal extraction is shared via react-jsx.ts.
|
|
8
|
+
*/
|
|
9
|
+
const PAGE_RE = /^page\.(tsx|jsx|ts|js)$/;
|
|
10
|
+
function appDirOf(projectRoot) {
|
|
11
|
+
return ([join(projectRoot, "app"), join(projectRoot, "src", "app")].find((d) => existsSync(d) && statSync(d).isDirectory()) ?? null);
|
|
12
|
+
}
|
|
13
|
+
export const nextAppAdapter = {
|
|
14
|
+
id: "next",
|
|
15
|
+
router: "next-app",
|
|
16
|
+
detect(projectRoot) {
|
|
17
|
+
return (appDirOf(projectRoot) !== null &&
|
|
18
|
+
!isReactRouterSpa(projectRoot) &&
|
|
19
|
+
!isReactNativeApp(projectRoot) &&
|
|
20
|
+
!isVueApp(projectRoot) &&
|
|
21
|
+
!isAndroidApp(projectRoot) &&
|
|
22
|
+
!isRemixApp(projectRoot) &&
|
|
23
|
+
!isAstroApp(projectRoot) &&
|
|
24
|
+
!isAngularApp(projectRoot) &&
|
|
25
|
+
!isRailsApp(projectRoot));
|
|
26
|
+
},
|
|
27
|
+
discover(projectRoot) {
|
|
28
|
+
const appDir = appDirOf(projectRoot);
|
|
29
|
+
if (!appDir)
|
|
30
|
+
return [];
|
|
31
|
+
const files = [];
|
|
32
|
+
walk(appDir, (file) => {
|
|
33
|
+
const base = file.slice(file.lastIndexOf(sep) + 1);
|
|
34
|
+
if (!PAGE_RE.test(base))
|
|
35
|
+
return;
|
|
36
|
+
const route = routeFromAppFile(appDir, file);
|
|
37
|
+
files.push({
|
|
38
|
+
absPath: file,
|
|
39
|
+
relPath: relative(projectRoot, file).split(sep).join("/"),
|
|
40
|
+
id: route,
|
|
41
|
+
route,
|
|
42
|
+
title: titleFromRoute(route),
|
|
43
|
+
isEntry: route === "/", // app-router entry point = root route
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
files.sort((a, b) => a.route.localeCompare(b.route));
|
|
47
|
+
return files;
|
|
48
|
+
},
|
|
49
|
+
parse(file) {
|
|
50
|
+
return parseReactScreen(sourceFileFor(file.absPath));
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
/** app-router file path → route. Strips route groups `(x)`, keeps dynamic `[slug]`. */
|
|
54
|
+
function routeFromAppFile(appDir, file) {
|
|
55
|
+
const segs = relative(appDir, file)
|
|
56
|
+
.split(sep)
|
|
57
|
+
.slice(0, -1)
|
|
58
|
+
.filter((s) => !(s.startsWith("(") && s.endsWith(")")));
|
|
59
|
+
const route = "/" + segs.join("/");
|
|
60
|
+
return route.length > 1 ? route.replace(/\/+$/, "") : "/";
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=next-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-app.js","sourceRoot":"","sources":["../../../src/core/adapters/next-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { statSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
import { sourceFileFor, walk, parseReactScreen, titleFromRoute, isReactRouterSpa, isReactNativeApp, isVueApp, isAndroidApp, isRemixApp, isAstroApp, isAngularApp, isRailsApp } from "./react-jsx.js";
|
|
4
|
+
/**
|
|
5
|
+
* Next.js pages-router adapter: treats `pages/**/*.tsx` as screens (one file = one
|
|
6
|
+
* route), parses with ts-morph. Screen id = route ("/blog/[slug]"). Entry = "/".
|
|
7
|
+
* Excludes API routes and the framework files (_app/_document/_error).
|
|
8
|
+
* JSX signal extraction is shared via react-jsx.ts; only file→route differs from next-app.
|
|
9
|
+
*/
|
|
10
|
+
const PAGE_RE = /\.(tsx|jsx|ts|js)$/;
|
|
11
|
+
const SPECIAL = /^_(app|document|error)$/;
|
|
12
|
+
function pagesDirOf(projectRoot) {
|
|
13
|
+
return ([join(projectRoot, "pages"), join(projectRoot, "src", "pages")].find((d) => existsSync(d) && statSync(d).isDirectory()) ?? null);
|
|
14
|
+
}
|
|
15
|
+
export const nextPagesAdapter = {
|
|
16
|
+
id: "next",
|
|
17
|
+
router: "next-pages",
|
|
18
|
+
detect(projectRoot) {
|
|
19
|
+
return (pagesDirOf(projectRoot) !== null &&
|
|
20
|
+
!isReactRouterSpa(projectRoot) &&
|
|
21
|
+
!isReactNativeApp(projectRoot) &&
|
|
22
|
+
!isVueApp(projectRoot) &&
|
|
23
|
+
!isAndroidApp(projectRoot) &&
|
|
24
|
+
!isRemixApp(projectRoot) &&
|
|
25
|
+
!isAstroApp(projectRoot) &&
|
|
26
|
+
!isAngularApp(projectRoot) &&
|
|
27
|
+
!isRailsApp(projectRoot));
|
|
28
|
+
},
|
|
29
|
+
discover(projectRoot) {
|
|
30
|
+
const pagesDir = pagesDirOf(projectRoot);
|
|
31
|
+
if (!pagesDir)
|
|
32
|
+
return [];
|
|
33
|
+
const apiDir = join(pagesDir, "api");
|
|
34
|
+
const files = [];
|
|
35
|
+
walk(pagesDir, (file) => {
|
|
36
|
+
if (file.startsWith(apiDir + sep))
|
|
37
|
+
return; // API routes are not screens
|
|
38
|
+
if (!PAGE_RE.test(file))
|
|
39
|
+
return;
|
|
40
|
+
const base = file.slice(file.lastIndexOf(sep) + 1).replace(PAGE_RE, "");
|
|
41
|
+
if (SPECIAL.test(base))
|
|
42
|
+
return; // _app / _document / _error
|
|
43
|
+
const route = routeFromPageFile(pagesDir, file);
|
|
44
|
+
files.push({
|
|
45
|
+
absPath: file,
|
|
46
|
+
relPath: relative(projectRoot, file).split(sep).join("/"),
|
|
47
|
+
id: route,
|
|
48
|
+
route,
|
|
49
|
+
title: titleFromRoute(route),
|
|
50
|
+
isEntry: route === "/", // pages-router entry point = root route
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
files.sort((a, b) => a.route.localeCompare(b.route));
|
|
54
|
+
return files;
|
|
55
|
+
},
|
|
56
|
+
parse(file) {
|
|
57
|
+
return parseReactScreen(sourceFileFor(file.absPath));
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/** pages-router file path → route. `index` drops to its dir, keeps dynamic `[slug]`/`[...all]`. */
|
|
61
|
+
function routeFromPageFile(pagesDir, file) {
|
|
62
|
+
const segs = relative(pagesDir, file)
|
|
63
|
+
.replace(PAGE_RE, "")
|
|
64
|
+
.split(sep);
|
|
65
|
+
if (segs[segs.length - 1] === "index")
|
|
66
|
+
segs.pop();
|
|
67
|
+
const route = "/" + segs.join("/");
|
|
68
|
+
return route.length > 1 ? route.replace(/\/+$/, "") : "/";
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=next-pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-pages.js","sourceRoot":"","sources":["../../../src/core/adapters/next-pages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { statSync, existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
import { walkVue, safeReadVue, parseVueSfc, titleFromRoute } from "./vue-sfc.js";
|
|
4
|
+
/**
|
|
5
|
+
* Nuxt adapter (Vue, file-based). `pages/**/*.vue` → routes, mirroring Nuxt's filesystem
|
|
6
|
+
* router. Screen id = route ("/users/[id]"); `index.vue` collapses to its dir; dynamic
|
|
7
|
+
* `[id]`/`[...slug]` kept. Entry = "/". SFC signal extraction is shared via vue-sfc.ts.
|
|
8
|
+
*/
|
|
9
|
+
function pagesDirOf(projectRoot) {
|
|
10
|
+
return ([join(projectRoot, "pages"), join(projectRoot, "src", "pages")].find((d) => existsSync(d) && statSync(d).isDirectory()) ?? null);
|
|
11
|
+
}
|
|
12
|
+
function hasNuxtDep(projectRoot) {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
|
|
15
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
16
|
+
return "nuxt" in deps;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const nuxtAdapter = {
|
|
23
|
+
id: "nuxt",
|
|
24
|
+
router: "nuxt-pages",
|
|
25
|
+
detect(projectRoot) {
|
|
26
|
+
return hasNuxtDep(projectRoot) && pagesDirOf(projectRoot) !== null;
|
|
27
|
+
},
|
|
28
|
+
discover(projectRoot) {
|
|
29
|
+
const pagesDir = pagesDirOf(projectRoot);
|
|
30
|
+
if (!pagesDir)
|
|
31
|
+
return [];
|
|
32
|
+
const files = [];
|
|
33
|
+
walkVue(pagesDir, (file) => {
|
|
34
|
+
const route = routeFromPageFile(pagesDir, file);
|
|
35
|
+
files.push({
|
|
36
|
+
absPath: file,
|
|
37
|
+
relPath: relative(projectRoot, file).split(sep).join("/"),
|
|
38
|
+
id: route,
|
|
39
|
+
route,
|
|
40
|
+
title: titleFromRoute(route),
|
|
41
|
+
isEntry: route === "/",
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
files.sort((a, b) => a.route.localeCompare(b.route));
|
|
45
|
+
return files;
|
|
46
|
+
},
|
|
47
|
+
parse(file) {
|
|
48
|
+
return parseVueSfc(safeReadVue(file.absPath));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
/** Nuxt page file → route. `index` collapses to its dir; keeps dynamic `[id]`/`[...slug]`. */
|
|
52
|
+
function routeFromPageFile(pagesDir, file) {
|
|
53
|
+
const segs = relative(pagesDir, file).replace(/\.vue$/, "").split(sep);
|
|
54
|
+
if (segs[segs.length - 1] === "index")
|
|
55
|
+
segs.pop();
|
|
56
|
+
const route = "/" + segs.join("/");
|
|
57
|
+
return route.length > 1 ? route.replace(/\/+$/, "") : "/";
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=nuxt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nuxt.js","sourceRoot":"","sources":["../../../src/core/adapters/nuxt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEjF;;;;GAIG;AACH,SAAS,UAAU,CAAC,WAAmB;IACrC,OAAO,CACL,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAClE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAClD,IAAI,IAAI,CACV,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,WAAmB;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAG7E,CAAC;QACF,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;QAC7D,OAAO,MAAM,IAAI,IAAI,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAqB;IAC3C,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,YAAY;IAEpB,MAAM,CAAC,WAAW;QAChB,OAAO,UAAU,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACrE,CAAC;IAED,QAAQ,CAAC,WAAW;QAClB,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QACzB,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC;gBACT,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzD,EAAE,EAAE,KAAK;gBACT,KAAK;gBACL,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC;gBAC5B,OAAO,EAAE,KAAK,KAAK,GAAG;aACvB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;CACF,CAAC;AAEF,8FAA8F;AAC9F,SAAS,iBAAiB,CAAC,QAAgB,EAAE,IAAY;IACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,OAAO;QAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
// path-helper name (without the _path/_url suffix) → route, rebuilt each discover() and read by parse().
|
|
4
|
+
let helperToRoute = new Map();
|
|
5
|
+
let routeToInfo = new Map();
|
|
6
|
+
export const railsAdapter = {
|
|
7
|
+
id: "rails",
|
|
8
|
+
router: "rails-routes",
|
|
9
|
+
detect(projectRoot) {
|
|
10
|
+
if (existsSync(join(projectRoot, "config", "routes.rb")))
|
|
11
|
+
return true;
|
|
12
|
+
const gemfile = join(projectRoot, "Gemfile");
|
|
13
|
+
return existsSync(gemfile) && /\bgem\s+["']rails["']/.test(safeRead(gemfile));
|
|
14
|
+
},
|
|
15
|
+
discover(projectRoot) {
|
|
16
|
+
helperToRoute = new Map();
|
|
17
|
+
routeToInfo = new Map();
|
|
18
|
+
const routesFile = findRoutesFile(projectRoot);
|
|
19
|
+
if (!routesFile)
|
|
20
|
+
return [];
|
|
21
|
+
const routes = parseRoutes(safeRead(routesFile));
|
|
22
|
+
const files = [];
|
|
23
|
+
const usedRelPaths = new Set();
|
|
24
|
+
for (const r of routes) {
|
|
25
|
+
if (routeToInfo.has(r.route))
|
|
26
|
+
continue; // first declaration wins
|
|
27
|
+
const viewFile = resolveView(projectRoot, r.controller, r.action);
|
|
28
|
+
const info = { ...r, viewFile };
|
|
29
|
+
routeToInfo.set(r.route, info);
|
|
30
|
+
for (const h of r.helpers)
|
|
31
|
+
helperToRoute.set(h, r.route);
|
|
32
|
+
const repr = viewFile ?? join(projectRoot, "config", "routes.rb");
|
|
33
|
+
let relPath = relative(projectRoot, repr).split(sep).join("/");
|
|
34
|
+
if (usedRelPaths.has(relPath))
|
|
35
|
+
relPath = `${relPath}#${r.route}`;
|
|
36
|
+
usedRelPaths.add(relPath);
|
|
37
|
+
files.push({
|
|
38
|
+
absPath: repr,
|
|
39
|
+
relPath,
|
|
40
|
+
id: r.route,
|
|
41
|
+
route: r.route,
|
|
42
|
+
title: titleFromRoute(r.route, `${r.controller}#${r.action}`),
|
|
43
|
+
isEntry: r.route === "/",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
files.sort((a, b) => a.route.localeCompare(b.route));
|
|
47
|
+
return files;
|
|
48
|
+
},
|
|
49
|
+
parse(file) {
|
|
50
|
+
const info = routeToInfo.get(file.id);
|
|
51
|
+
const controllerSrc = info ? actionBody(controllerFile(file, info), info.action) : "";
|
|
52
|
+
const viewSrc = info?.viewFile ? safeRead(info.viewFile) : "";
|
|
53
|
+
return parseRails(controllerSrc, viewSrc);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const ROOT_RE = /^root\s+(?:to:\s*)?["']([\w/]+)#(\w+)["']/;
|
|
57
|
+
const VERB_RE = /^(?:get|post|put|patch|delete)\s+["']([^"']+)["']\s*(?:,\s*(?:to:\s*)?|=>\s*)["']([\w/]+)#(\w+)["']/;
|
|
58
|
+
const RESOURCES_RE = /^resources?\s+:(\w+)/;
|
|
59
|
+
const RESOURCE_SINGULAR_RE = /^resource\s+:(\w+)/;
|
|
60
|
+
function parseRoutes(src) {
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const rawLine of src.split(/\r?\n/)) {
|
|
63
|
+
const line = rawLine.trim();
|
|
64
|
+
if (!line || line.startsWith("#"))
|
|
65
|
+
continue;
|
|
66
|
+
const root = line.match(ROOT_RE);
|
|
67
|
+
if (root) {
|
|
68
|
+
out.push({ route: "/", controller: root[1], action: root[2], helpers: ["root"] });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const verb = line.match(VERB_RE);
|
|
72
|
+
if (verb) {
|
|
73
|
+
const path = normalize(verb[1]);
|
|
74
|
+
out.push({ route: path, controller: verb[2], action: verb[3], helpers: [helperFromPath(path)] });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const res = line.match(RESOURCES_RE);
|
|
78
|
+
if (res && !RESOURCE_SINGULAR_RE.test(line)) {
|
|
79
|
+
out.push(...expandResources(res[1]));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const sres = line.match(RESOURCE_SINGULAR_RE);
|
|
83
|
+
if (sres)
|
|
84
|
+
out.push(...expandSingularResource(sres[1]));
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/** `resources :posts` → the 7 RESTful routes with their path helpers. */
|
|
89
|
+
function expandResources(name) {
|
|
90
|
+
const c = name; // controller = plural resource name
|
|
91
|
+
const singular = singularize(name);
|
|
92
|
+
return [
|
|
93
|
+
{ route: `/${name}`, controller: c, action: "index", helpers: [name] },
|
|
94
|
+
{ route: `/${name}/new`, controller: c, action: "new", helpers: [`new_${singular}`] },
|
|
95
|
+
{ route: `/${name}/:id`, controller: c, action: "show", helpers: [singular] },
|
|
96
|
+
{ route: `/${name}/:id/edit`, controller: c, action: "edit", helpers: [`edit_${singular}`] },
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
/** `resource :profile` (singular) → show/new/edit (no index, no :id). */
|
|
100
|
+
function expandSingularResource(name) {
|
|
101
|
+
const plural = name + "s";
|
|
102
|
+
return [
|
|
103
|
+
{ route: `/${name}`, controller: plural, action: "show", helpers: [name] },
|
|
104
|
+
{ route: `/${name}/new`, controller: plural, action: "new", helpers: [`new_${name}`] },
|
|
105
|
+
{ route: `/${name}/edit`, controller: plural, action: "edit", helpers: [`edit_${name}`] },
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
// ---------- controller + view resolution ----------
|
|
109
|
+
function findRoutesFile(projectRoot) {
|
|
110
|
+
const f = join(projectRoot, "config", "routes.rb");
|
|
111
|
+
if (existsSync(f))
|
|
112
|
+
return f;
|
|
113
|
+
let hit = null;
|
|
114
|
+
walkRb(projectRoot, (file) => {
|
|
115
|
+
if (!hit && file.endsWith("routes.rb"))
|
|
116
|
+
hit = file;
|
|
117
|
+
});
|
|
118
|
+
return hit;
|
|
119
|
+
}
|
|
120
|
+
/** app/views/<controller>/<action>.html.erb (also .erb / .haml fallbacks). */
|
|
121
|
+
function resolveView(projectRoot, controller, action) {
|
|
122
|
+
const bases = [
|
|
123
|
+
join(projectRoot, "app", "views", controller, action),
|
|
124
|
+
join(projectRoot, "views", controller, action),
|
|
125
|
+
];
|
|
126
|
+
const exts = [".html.erb", ".html.haml", ".erb", ".haml"];
|
|
127
|
+
for (const b of bases)
|
|
128
|
+
for (const e of exts)
|
|
129
|
+
if (existsSync(b + e))
|
|
130
|
+
return b + e;
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/** app/controllers/<controller>_controller.rb. */
|
|
134
|
+
function controllerFile(file, info) {
|
|
135
|
+
// viewFile path: …/app/views/<controller>/<action>… → …/app/controllers/<controller>_controller.rb
|
|
136
|
+
const abs = file.absPath;
|
|
137
|
+
const viewsIdx = abs.lastIndexOf(`${sep}views${sep}`);
|
|
138
|
+
if (viewsIdx >= 0) {
|
|
139
|
+
const appRoot = abs.slice(0, viewsIdx);
|
|
140
|
+
const cf = join(appRoot, "controllers", `${info.controller}_controller.rb`);
|
|
141
|
+
if (existsSync(cf))
|
|
142
|
+
return cf;
|
|
143
|
+
}
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
/** Body of `def <action>` up to the matching `end` (simple indent-agnostic: next `def`/class `end`). */
|
|
147
|
+
function actionBody(controllerPath, action) {
|
|
148
|
+
if (!controllerPath)
|
|
149
|
+
return "";
|
|
150
|
+
const lines = safeRead(controllerPath).split("\n");
|
|
151
|
+
const start = lines.findIndex((l) => new RegExp(`^\\s*def\\s+${action}\\b`).test(l));
|
|
152
|
+
if (start < 0)
|
|
153
|
+
return "";
|
|
154
|
+
const out = [lines[start]];
|
|
155
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
156
|
+
if (/^\s*def\s+\w/.test(lines[i]))
|
|
157
|
+
break; // next action
|
|
158
|
+
out.push(lines[i]);
|
|
159
|
+
}
|
|
160
|
+
return out.join("\n");
|
|
161
|
+
}
|
|
162
|
+
// ---------- signal extraction (controller action + view ERB) ----------
|
|
163
|
+
const LINK_TO = /\blink_to\b[^,]*,\s*(\w+)_(?:path|url)\b/; // link_to 'x', posts_path
|
|
164
|
+
const LINK_TO_LITERAL = /\blink_to\b[^,]*,\s*["'](\/[^"']*)["']/;
|
|
165
|
+
const HREF = /<a\b[^>]*?\shref=["']([^"'#<]+)["']/i;
|
|
166
|
+
const REDIRECT_HELPER = /\bredirect_to\s+(\w+)_(?:path|url)\b/;
|
|
167
|
+
const REDIRECT_LITERAL = /\bredirect_to\s+["'](\/[^"']*)["']/;
|
|
168
|
+
const HTTP_CALL = /\b(?:Net::HTTP|HTTParty|Faraday)[.\w]*\.(get|post|put|delete)\b|\bHTTParty\.(get|post)\s*\(\s*["']([^"']+)["']/;
|
|
169
|
+
const AR_CALL = /\b([A-Z]\w*)\.(all|where|find|find_by|create|count|first|last)\b/;
|
|
170
|
+
const FEATURE_RULES = [
|
|
171
|
+
{ re: /<button\b[^>]*>([^<]*)/i, kind: "button", label: (m) => m[1].trim() || "Button" },
|
|
172
|
+
{ re: /\bsubmit\b/i, kind: "button", label: () => "Submit" },
|
|
173
|
+
{ re: /<(?:input|textarea|select)\b/i, kind: "input", label: () => "Input" },
|
|
174
|
+
{ re: /\b(?:text_field|text_area|select|number_field|email_field)\b/, kind: "input", label: () => "Input" },
|
|
175
|
+
{ re: /\b(?:form_with|form_for|form_tag)\b|<form\b/i, kind: "form", label: () => "Form" },
|
|
176
|
+
];
|
|
177
|
+
function parseRails(controllerSrc, viewSrc) {
|
|
178
|
+
const navs = [];
|
|
179
|
+
const calls = [];
|
|
180
|
+
const features = [];
|
|
181
|
+
// --- controller action: redirects + ActiveRecord / HTTP calls ---
|
|
182
|
+
controllerSrc.split(/\r?\n/).forEach((rawLine, i) => {
|
|
183
|
+
const line = rawLine.trim();
|
|
184
|
+
const redH = line.match(REDIRECT_HELPER);
|
|
185
|
+
const redL = line.match(REDIRECT_LITERAL);
|
|
186
|
+
if (redH)
|
|
187
|
+
navs.push({ target: helperToRoute.get(redH[1]) ?? null, raw: snippet(line), trigger: "redirect_to", line: i + 1 });
|
|
188
|
+
else if (redL)
|
|
189
|
+
navs.push({ target: normalize(redL[1]), raw: snippet(line), trigger: "redirect_to", line: i + 1 });
|
|
190
|
+
const ar = line.match(AR_CALL);
|
|
191
|
+
if (ar)
|
|
192
|
+
calls.push({ url: `${ar[1]}.${ar[2]}`, raw: snippet(line), trigger: "activerecord", line: i + 1 });
|
|
193
|
+
const httpM = line.match(HTTP_CALL);
|
|
194
|
+
if (httpM)
|
|
195
|
+
calls.push({ url: httpM[3] ?? null, method: (httpM[1] ?? httpM[2] ?? "GET").toUpperCase(), raw: snippet(line), trigger: "http", line: i + 1 });
|
|
196
|
+
});
|
|
197
|
+
// --- view ERB: link_to / <a href> nav, form features ---
|
|
198
|
+
viewSrc.split(/\r?\n/).forEach((rawLine, i) => {
|
|
199
|
+
const line = rawLine.trim();
|
|
200
|
+
const linkH = line.match(LINK_TO);
|
|
201
|
+
const linkL = line.match(LINK_TO_LITERAL);
|
|
202
|
+
const href = line.match(HREF);
|
|
203
|
+
if (linkH)
|
|
204
|
+
navs.push({ target: helperToRoute.get(linkH[1]) ?? null, raw: snippet(line), trigger: "link_to", line: i + 1 });
|
|
205
|
+
else if (linkL)
|
|
206
|
+
navs.push({ target: normalize(linkL[1]), raw: snippet(line), trigger: "link_to", line: i + 1 });
|
|
207
|
+
else if (href)
|
|
208
|
+
navs.push({ target: normalize(href[1]), raw: snippet(line), trigger: "<a href>", line: i + 1 });
|
|
209
|
+
for (const rule of FEATURE_RULES) {
|
|
210
|
+
const m = line.match(rule.re);
|
|
211
|
+
if (m) {
|
|
212
|
+
features.push({ kind: rule.kind, label: rule.label(m), detail: m[0].trim().slice(0, 40), line: i + 1 });
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return { navs, calls, features, components: [], contains: [] };
|
|
218
|
+
}
|
|
219
|
+
// ---------- helpers ----------
|
|
220
|
+
function helperFromPath(path) {
|
|
221
|
+
// "/about" → "about"; "/" → "root"; strip params, join segments with _.
|
|
222
|
+
const segs = path.split("/").filter((s) => s && !s.startsWith(":"));
|
|
223
|
+
return segs.length ? segs.join("_") : "root";
|
|
224
|
+
}
|
|
225
|
+
function singularize(name) {
|
|
226
|
+
if (name.endsWith("ies"))
|
|
227
|
+
return name.slice(0, -3) + "y";
|
|
228
|
+
if (name.endsWith("ses"))
|
|
229
|
+
return name.slice(0, -2);
|
|
230
|
+
if (name.endsWith("s"))
|
|
231
|
+
return name.slice(0, -1);
|
|
232
|
+
return name;
|
|
233
|
+
}
|
|
234
|
+
function normalize(path) {
|
|
235
|
+
let r = path.startsWith("/") ? path : "/" + path;
|
|
236
|
+
r = r.replace(/\/{2,}/g, "/");
|
|
237
|
+
return r.length > 1 ? r.replace(/\/+$/, "") : "/";
|
|
238
|
+
}
|
|
239
|
+
function titleFromRoute(route, ca) {
|
|
240
|
+
if (route === "/")
|
|
241
|
+
return "Home";
|
|
242
|
+
const last = route.split("/").filter((s) => s && !s.startsWith(":")).pop() ?? ca;
|
|
243
|
+
return last.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
244
|
+
}
|
|
245
|
+
function walkRb(dir, onFile) {
|
|
246
|
+
let entries;
|
|
247
|
+
try {
|
|
248
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
for (const entry of entries) {
|
|
254
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor" || entry.name === "tmp")
|
|
255
|
+
continue;
|
|
256
|
+
const full = join(dir, entry.name);
|
|
257
|
+
if (entry.isDirectory())
|
|
258
|
+
walkRb(full, onFile);
|
|
259
|
+
else if (entry.name.endsWith(".rb"))
|
|
260
|
+
onFile(full);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function safeRead(file) {
|
|
264
|
+
try {
|
|
265
|
+
return readFileSync(file, "utf8");
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return "";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function snippet(text) {
|
|
272
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
273
|
+
return flat.length > 80 ? flat.slice(0, 77) + "..." : flat;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=rails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rails.js","sourceRoot":"","sources":["../../../src/core/adapters/rails.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Project } from "ts-morph";
|
|
2
|
+
import type { SourceFile } from "ts-morph";
|
|
3
|
+
import type { RawScreen } from "./types.js";
|
|
4
|
+
/** Shared ts-morph project (lazy). allowJs + React JSX so .jsx/.tsx all parse. */
|
|
5
|
+
export declare function project(): Project;
|
|
6
|
+
/** Add a file to the shared project, reusing it if already added (parse is idempotent). */
|
|
7
|
+
export declare function sourceFileFor(absPath: string): SourceFile;
|
|
8
|
+
/** True if any of `names` is a (dev)dependency in the project's package.json. */
|
|
9
|
+
export declare function hasDependency(projectRoot: string, ...names: string[]): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A non-Next React SPA that happens to use a `pages/`-style folder shouldn't be read as
|
|
12
|
+
* Next.js. True when react-router is a dependency and Next.js is not — the Next adapters
|
|
13
|
+
* bow out in that case (the dependency signal beats the directory-name heuristic).
|
|
14
|
+
*/
|
|
15
|
+
export declare function isReactRouterSpa(projectRoot: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* A React Native / Expo project. Expo Router uses an `app/` dir just like Next's app-router,
|
|
18
|
+
* so the Next adapters must bow out when this is true (the dependency signal disambiguates).
|
|
19
|
+
*/
|
|
20
|
+
export declare function isReactNativeApp(projectRoot: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* A Remix / React Router 7 (framework mode) project. Routes live in `app/routes/`, so the
|
|
23
|
+
* `app/` dir would otherwise make the Next app-router adapter claim it — the dependency signal
|
|
24
|
+
* (`@remix-run/*` or `@react-router/dev`) disambiguates, so the Next adapters bow out.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isRemixApp(projectRoot: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* A Vue/Nuxt project. Nuxt also uses a `pages/` dir, which would otherwise make the Next
|
|
29
|
+
* pages-router adapter claim it — so the Next adapters bow out when this is true.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isVueApp(projectRoot: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* An Astro project. Astro uses `src/pages/`, which the Next pages-router adapter would otherwise
|
|
34
|
+
* claim — so the Next adapters bow out when `astro` is a dependency.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isAstroApp(projectRoot: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* An Angular project. Angular's conventional `src/app/` dir collides with Next's app-router,
|
|
39
|
+
* so the Next adapters bow out when `@angular/core`/`@angular/router` is a dependency.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isAngularApp(projectRoot: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* A Rails project. Rails' `app/` dir collides with Next's app-router; detect by `config/routes.rb`
|
|
44
|
+
* (Rails has no package.json) or a Gemfile that pulls in rails, so the Next adapters bow out.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isRailsApp(projectRoot: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* An Android/Compose project. Gradle's `app/` module dir collides with Next's app-router
|
|
49
|
+
* convention, so the Next adapters must bow out. Android projects have no package.json, so
|
|
50
|
+
* detect by Gradle build files (build.gradle / build.gradle.kts / settings.gradle*).
|
|
51
|
+
*/
|
|
52
|
+
export declare function isAndroidApp(projectRoot: string): boolean;
|
|
53
|
+
/** React/JS source extensions, in resolution-priority order. */
|
|
54
|
+
export declare const SOURCE_EXTS: string[];
|
|
55
|
+
/**
|
|
56
|
+
* Map each importable name in a file to its module specifier — covers default and named
|
|
57
|
+
* static imports plus `const X = lazy(() => import("…"))`. Used to turn a route's referenced
|
|
58
|
+
* component identifier back into the file it lives in.
|
|
59
|
+
*/
|
|
60
|
+
export declare function importMap(sf: SourceFile): Map<string, string>;
|
|
61
|
+
/** Resolve a relative module specifier (from `configDir`) to an on-disk source file, or null. */
|
|
62
|
+
export declare function resolveComponentFile(configDir: string, spec: string | undefined): string | null;
|
|
63
|
+
/** Recursively visit every file under `dir`, skipping node_modules and dotfiles. */
|
|
64
|
+
export declare function walk(dir: string, onFile: (file: string) => void): void;
|
|
65
|
+
/** Last path segment → Title Case, unwrapping dynamic `[slug]`/`:slug`. Shared route→title. */
|
|
66
|
+
export declare function titleFromRoute(route: string): string;
|
|
67
|
+
/** Parse one React/JSX screen file into raw nav/call/feature signals. */
|
|
68
|
+
export declare function parseReactScreen(sf: SourceFile): RawScreen;
|