jskelet 0.6.1 → 0.6.3
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/AGENTS.md +136 -136
- package/CHANGELOG.md +620 -596
- package/LICENSE +21 -21
- package/bin/jskelet.mjs +130 -130
- package/docs/01-baslangic.md +291 -291
- package/docs/02-mimari.md +310 -309
- package/docs/03-routing.md +515 -515
- package/docs/04-render-ve-sablonlar.md +661 -661
- package/docs/05-islands.md +486 -486
- package/docs/06-cache.md +1443 -1423
- package/docs/07-yapilandirma.md +12 -6
- package/docs/08-build.md +429 -428
- package/docs/09-dev-araclari.md +364 -364
- package/docs/10-dagitim.md +338 -338
- package/docs/12-panel-ve-oturum.md +478 -478
- package/docs/README.md +83 -83
- package/docs/en/01-getting-started.md +298 -298
- package/docs/en/02-architecture.md +329 -328
- package/docs/en/03-routing.md +531 -531
- package/docs/en/04-rendering.md +669 -669
- package/docs/en/05-islands.md +497 -497
- package/docs/en/06-caching.md +1453 -1431
- package/docs/en/07-configuration.md +1219 -1214
- package/docs/en/08-build.md +447 -446
- package/docs/en/09-dev-tools.md +373 -373
- package/docs/en/10-deployment.md +340 -340
- package/docs/en/11-migration.md +398 -398
- package/docs/en/12-dashboards-and-sessions.md +488 -488
- package/docs/en/README.md +87 -87
- package/package.json +137 -137
- package/src/build/ensure-build.mjs +19 -19
- package/src/build/paths.mjs +153 -153
- package/src/build/resolve-peer.mjs +36 -36
- package/src/build/tasks/client.mjs +349 -349
- package/src/build/tasks/css.mjs +235 -235
- package/src/build/tasks/fonts.mjs +146 -146
- package/src/build/tasks/icons.mjs +357 -357
- package/src/build/tasks/images.mjs +244 -244
- package/src/build/tasks/precompress.mjs +78 -78
- package/src/build/tasks/templates.mjs +20 -20
- package/src/client/admin/i18n.js +764 -764
- package/src/client/admin/login.html +74 -74
- package/src/client/admin/panel.css +809 -809
- package/src/client/admin/panel.html +495 -495
- package/src/client/admin/panel.js +1251 -1251
- package/src/client/devtools/report.html +185 -185
- package/src/client/devtools/report.js +745 -745
- package/src/client/devtools/seo.js +628 -628
- package/src/client/dom.js +95 -95
- package/src/client/form.js +192 -192
- package/src/client/index.js +45 -45
- package/src/client/registry.js +305 -305
- package/src/client/safe-image.js +91 -91
- package/src/client/shared-cookie.js +225 -225
- package/src/client/store.js +36 -36
- package/src/client/swap.js +188 -188
- package/src/compile/codegen.js +336 -336
- package/src/compile/compile-all.js +149 -149
- package/src/compile/errors.js +66 -66
- package/src/compile/expr.js +409 -409
- package/src/compile/index.js +17 -17
- package/src/compile/parse.js +541 -541
- package/src/compile/resolve.js +211 -211
- package/src/compile/scan-exports.js +51 -51
- package/src/config/defaults.js +17 -1
- package/src/config/index.js +13 -0
- package/src/config/pattern.js +107 -107
- package/src/generate.mjs +163 -163
- package/src/http/control-flow.js +71 -71
- package/src/http/cookies-entry.js +21 -21
- package/src/http/cookies.js +277 -277
- package/src/http/request-cache.js +46 -46
- package/src/http/request-context.js +165 -165
- package/src/http/shared-cookie.js +178 -178
- package/src/index.js +101 -101
- package/src/init.mjs +230 -230
- package/src/migrate/apply.mjs +262 -262
- package/src/migrate/babel.mjs +79 -79
- package/src/migrate/classify.mjs +155 -155
- package/src/migrate/config.mjs +126 -126
- package/src/migrate/fs-walk.mjs +191 -191
- package/src/migrate/parse.mjs +26 -26
- package/src/migrate/scan.mjs +177 -177
- package/src/migrate/transform/expr-source.mjs +168 -168
- package/src/migrate/transform/island.mjs +67 -67
- package/src/migrate/transform/jsx-to-component.mjs +302 -302
- package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
- package/src/migrate/transform/page-split.mjs +435 -435
- package/src/migrate/write.mjs +81 -81
- package/src/migrate.mjs +171 -171
- package/src/runtime/alias-hooks.mjs +119 -119
- package/src/runtime/register.mjs +4 -4
- package/src/server/admin/actions.js +229 -229
- package/src/server/admin/auth.js +125 -125
- package/src/server/admin/event-log.js +151 -151
- package/src/server/admin/gate.js +209 -209
- package/src/server/admin/inventory.js +188 -188
- package/src/server/admin/mount.js +56 -56
- package/src/server/admin/router.js +216 -216
- package/src/server/admin/snapshot.js +241 -241
- package/src/server/assets.js +147 -147
- package/src/server/auth/handoff.js +309 -309
- package/src/server/cache-blob.js +70 -0
- package/src/server/cache-deps.js +42 -42
- package/src/server/cache-vary.js +113 -113
- package/src/server/cloudflare.js +607 -607
- package/src/server/create-app.js +366 -366
- package/src/server/data-cache.js +553 -462
- package/src/server/dev/report.js +485 -485
- package/src/server/dev/socket.js +170 -170
- package/src/server/dev/version-check.mjs +139 -139
- package/src/server/disk-cache.js +233 -0
- package/src/server/ejs-adapter.js +59 -59
- package/src/server/html-cache.js +1196 -1122
- package/src/server/image-optimizer.js +500 -407
- package/src/server/logs/access-middleware.js +66 -66
- package/src/server/logs/file-sink.js +193 -66
- package/src/server/logs/pipeline.js +165 -158
- package/src/server/logs/s3-put.js +214 -214
- package/src/server/logs/s3-sink.js +112 -112
- package/src/server/metadata.js +102 -102
- package/src/server/middleware/compression.js +205 -205
- package/src/server/middleware/csrf.js +134 -134
- package/src/server/middleware/dev-gate.js +75 -75
- package/src/server/middleware/headers.js +37 -37
- package/src/server/middleware/redirects.js +32 -32
- package/src/server/middleware/robots-txt.js +341 -341
- package/src/server/middleware/static-precompressed.js +121 -100
- package/src/server/middleware/trailing-slash.js +53 -53
- package/src/server/middleware/upstream-proxy.js +141 -141
- package/src/server/og-image.js +356 -356
- package/src/server/port-guard.js +255 -255
- package/src/server/prewarm.js +1082 -1058
- package/src/server/redis.js +588 -569
- package/src/server/render.js +4 -4
- package/src/server/router.js +157 -157
- package/src/server/status-page.js +265 -265
- package/src/server/upstream-limiter.js +376 -376
- package/src/server/upstream-tracking.js +166 -166
- package/src/shared/cookie-domain.js +66 -66
- package/src/start.mjs +22 -22
- package/src/templates/layout.ejs +30 -30
- package/src/templates/layout.jsk +30 -30
- package/src/version.mjs +31 -31
- package/src/views/components/loader.js +101 -101
- package/src/views/helpers/html.js +102 -102
- package/src/views/helpers/tags.js +375 -375
- package/types/config/defaults.d.ts +15 -1
- package/types/config/index.d.ts +8 -0
- package/types/server/cache-blob.d.ts +13 -0
- package/types/server/data-cache.d.ts +9 -0
- package/types/server/disk-cache.d.ts +36 -0
- package/types/server/html-cache.d.ts +26 -3
- package/types/server/logs/file-sink.d.ts +16 -5
- package/types/server/redis.d.ts +2 -1
package/src/migrate/parse.mjs
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Next.js / React kaynak dosyalarını Babel ile parse eder (JSX + TS).
|
|
3
|
-
*
|
|
4
|
-
* `@babel/parser` opsiyonel peerdır; yoksa `getBabel()` kurulum mesajı fırlatır.
|
|
5
|
-
*/
|
|
6
|
-
import { getBabel } from "./babel.mjs";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {string} code
|
|
10
|
-
* @param {string} [filename]
|
|
11
|
-
* @returns {import('@babel/types').File}
|
|
12
|
-
*/
|
|
13
|
-
export function parseSource(code, filename = "file.tsx") {
|
|
14
|
-
const { parse } = getBabel();
|
|
15
|
-
return parse(code, {
|
|
16
|
-
sourceType: "module",
|
|
17
|
-
sourceFilename: filename,
|
|
18
|
-
plugins: [
|
|
19
|
-
"jsx",
|
|
20
|
-
"typescript",
|
|
21
|
-
"importAttributes",
|
|
22
|
-
"topLevelAwait",
|
|
23
|
-
],
|
|
24
|
-
errorRecovery: false,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Next.js / React kaynak dosyalarını Babel ile parse eder (JSX + TS).
|
|
3
|
+
*
|
|
4
|
+
* `@babel/parser` opsiyonel peerdır; yoksa `getBabel()` kurulum mesajı fırlatır.
|
|
5
|
+
*/
|
|
6
|
+
import { getBabel } from "./babel.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} code
|
|
10
|
+
* @param {string} [filename]
|
|
11
|
+
* @returns {import('@babel/types').File}
|
|
12
|
+
*/
|
|
13
|
+
export function parseSource(code, filename = "file.tsx") {
|
|
14
|
+
const { parse } = getBabel();
|
|
15
|
+
return parse(code, {
|
|
16
|
+
sourceType: "module",
|
|
17
|
+
sourceFilename: filename,
|
|
18
|
+
plugins: [
|
|
19
|
+
"jsx",
|
|
20
|
+
"typescript",
|
|
21
|
+
"importAttributes",
|
|
22
|
+
"topLevelAwait",
|
|
23
|
+
],
|
|
24
|
+
errorRecovery: false,
|
|
25
|
+
});
|
|
26
|
+
}
|
package/src/migrate/scan.mjs
CHANGED
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Next App Router envanteri + engel listesi.
|
|
3
|
-
*/
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
findAppDir,
|
|
8
|
-
findNextConfig,
|
|
9
|
-
inventoryApp,
|
|
10
|
-
findClientEnvKeys,
|
|
11
|
-
walkFiles,
|
|
12
|
-
} from "./fs-walk.mjs";
|
|
13
|
-
import { ensureBabel } from "./babel.mjs";
|
|
14
|
-
import { classifySource } from "./classify.mjs";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @param {string} root
|
|
18
|
-
* @returns {object}
|
|
19
|
-
*/
|
|
20
|
-
export function scanProject(root) {
|
|
21
|
-
// Babel yoksa yüzlerce "parse error" yerine tek net kurulum mesajı.
|
|
22
|
-
ensureBabel(root);
|
|
23
|
-
|
|
24
|
-
const appDir = findAppDir(root);
|
|
25
|
-
const nextConfig = findNextConfig(root);
|
|
26
|
-
const clientEnv = findClientEnvKeys(root);
|
|
27
|
-
|
|
28
|
-
if (!appDir) {
|
|
29
|
-
return {
|
|
30
|
-
root,
|
|
31
|
-
appDir: null,
|
|
32
|
-
nextConfig,
|
|
33
|
-
clientEnv,
|
|
34
|
-
pages: [],
|
|
35
|
-
layouts: [],
|
|
36
|
-
clients: [],
|
|
37
|
-
components: [],
|
|
38
|
-
blockers: [
|
|
39
|
-
{
|
|
40
|
-
kind: "no-app-dir",
|
|
41
|
-
message: "No app/ or src/app/ directory found (App Router required).",
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
pagesRouter: fs.existsSync(path.join(root, "pages")),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const inv = inventoryApp(appDir);
|
|
49
|
-
/** @type {object[]} */
|
|
50
|
-
const pages = [];
|
|
51
|
-
/** @type {object[]} */
|
|
52
|
-
const clients = [];
|
|
53
|
-
/** @type {object[]} */
|
|
54
|
-
const components = [];
|
|
55
|
-
/** @type {object[]} */
|
|
56
|
-
const blockers = [];
|
|
57
|
-
|
|
58
|
-
if (fs.existsSync(path.join(root, "pages"))) {
|
|
59
|
-
blockers.push({
|
|
60
|
-
kind: "pages-router",
|
|
61
|
-
message: "pages/ directory present — Pages Router is out of scope for this codemod.",
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const nestedLayouts = inv.layouts.filter((l) => l.depth > 0);
|
|
66
|
-
if (nestedLayouts.length) {
|
|
67
|
-
blockers.push({
|
|
68
|
-
kind: "nested-layouts",
|
|
69
|
-
message: `${nestedLayouts.length} nested layout(s); JSkelet has a single layout — flatten manually.`,
|
|
70
|
-
files: nestedLayouts.map((l) => l.rel),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const page of inv.pages) {
|
|
75
|
-
const code = fs.readFileSync(page.abs, "utf8");
|
|
76
|
-
const classification = classifySource(code, page.rel);
|
|
77
|
-
pages.push({ ...page, classification });
|
|
78
|
-
if (classification.hasServerAction) {
|
|
79
|
-
blockers.push({
|
|
80
|
-
kind: "server-action",
|
|
81
|
-
file: page.rel,
|
|
82
|
-
message: "Server Actions have no equivalent — use app.post handlers.",
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
if (classification.hasSuspense) {
|
|
86
|
-
blockers.push({
|
|
87
|
-
kind: "suspense",
|
|
88
|
-
file: page.rel,
|
|
89
|
-
message: "Suspense / streaming is not supported — use fragment endpoints.",
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
for (const layout of inv.layouts) {
|
|
95
|
-
const code = fs.readFileSync(layout.abs, "utf8");
|
|
96
|
-
layout.classification = classifySource(code, layout.rel);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for (const src of inv.sources) {
|
|
100
|
-
const code = fs.readFileSync(src.abs, "utf8");
|
|
101
|
-
const classification = classifySource(code, src.rel);
|
|
102
|
-
if (classification.kind === "client" || classification.isClient) {
|
|
103
|
-
clients.push({ ...src, classification });
|
|
104
|
-
} else if (classification.kind === "server-component") {
|
|
105
|
-
components.push({ ...src, classification });
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for (const dir of ["components", "src/components", "lib/components"]) {
|
|
110
|
-
const abs = path.join(root, dir);
|
|
111
|
-
if (!fs.existsSync(abs)) continue;
|
|
112
|
-
walkFiles(abs, (_rel, fileAbs) => {
|
|
113
|
-
if (!/\.(jsx?|tsx?)$/.test(fileAbs)) return;
|
|
114
|
-
const fromRoot = path.relative(root, fileAbs).replace(/\\/g, "/");
|
|
115
|
-
const code = fs.readFileSync(fileAbs, "utf8");
|
|
116
|
-
const classification = classifySource(code, fromRoot);
|
|
117
|
-
if (classification.kind === "client" || classification.isClient) {
|
|
118
|
-
clients.push({ abs: fileAbs, rel: fromRoot, classification });
|
|
119
|
-
} else if (classification.kind === "server-component") {
|
|
120
|
-
components.push({ abs: fileAbs, rel: fromRoot, classification });
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
root,
|
|
127
|
-
appDir: path.relative(root, appDir).replace(/\\/g, "/"),
|
|
128
|
-
nextConfig: nextConfig ? path.relative(root, nextConfig).replace(/\\/g, "/") : null,
|
|
129
|
-
clientEnv,
|
|
130
|
-
pages,
|
|
131
|
-
layouts: inv.layouts,
|
|
132
|
-
clients,
|
|
133
|
-
components,
|
|
134
|
-
blockers,
|
|
135
|
-
pagesRouter: fs.existsSync(path.join(root, "pages")),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* İnsan-okur özet.
|
|
141
|
-
* @param {ReturnType<typeof scanProject>} report
|
|
142
|
-
* @returns {string}
|
|
143
|
-
*/
|
|
144
|
-
export function formatScanReport(report) {
|
|
145
|
-
const lines = [];
|
|
146
|
-
lines.push(`Next.js → JSkelet scan`);
|
|
147
|
-
lines.push(` app: ${report.appDir ?? "(missing)"}`);
|
|
148
|
-
lines.push(` config: ${report.nextConfig ?? "(none)"}`);
|
|
149
|
-
lines.push(` pages: ${report.pages.length}`);
|
|
150
|
-
lines.push(` layouts: ${report.layouts.length}`);
|
|
151
|
-
lines.push(` client modules: ${report.clients.length}`);
|
|
152
|
-
lines.push(` server components: ${report.components.length}`);
|
|
153
|
-
if (report.clientEnv.length) {
|
|
154
|
-
lines.push(` NEXT_PUBLIC_* → clientEnv candidates: ${report.clientEnv.join(", ")}`);
|
|
155
|
-
}
|
|
156
|
-
if (report.pages.length) {
|
|
157
|
-
lines.push("");
|
|
158
|
-
lines.push("Pages:");
|
|
159
|
-
for (const p of report.pages) {
|
|
160
|
-
const flags = [];
|
|
161
|
-
if (p.classification?.revalidate != null) flags.push(`revalidate=${p.classification.revalidate}`);
|
|
162
|
-
if (p.classification?.hasGenerateMetadata) flags.push("metadata");
|
|
163
|
-
if (p.classification?.hasGenerateStaticParams) flags.push("staticParams");
|
|
164
|
-
lines.push(` ${p.url} ← ${p.rel}${flags.length ? ` (${flags.join(", ")})` : ""}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (report.blockers.length) {
|
|
168
|
-
lines.push("");
|
|
169
|
-
lines.push("Blockers:");
|
|
170
|
-
for (const b of report.blockers) {
|
|
171
|
-
lines.push(` ⚠ ${b.message}${b.file ? ` [${b.file}]` : ""}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
lines.push("");
|
|
175
|
-
lines.push("Next: jskelet migrate apply [--write] | jskelet migrate config [--write]");
|
|
176
|
-
return lines.join("\n");
|
|
177
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Next App Router envanteri + engel listesi.
|
|
3
|
+
*/
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
findAppDir,
|
|
8
|
+
findNextConfig,
|
|
9
|
+
inventoryApp,
|
|
10
|
+
findClientEnvKeys,
|
|
11
|
+
walkFiles,
|
|
12
|
+
} from "./fs-walk.mjs";
|
|
13
|
+
import { ensureBabel } from "./babel.mjs";
|
|
14
|
+
import { classifySource } from "./classify.mjs";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} root
|
|
18
|
+
* @returns {object}
|
|
19
|
+
*/
|
|
20
|
+
export function scanProject(root) {
|
|
21
|
+
// Babel yoksa yüzlerce "parse error" yerine tek net kurulum mesajı.
|
|
22
|
+
ensureBabel(root);
|
|
23
|
+
|
|
24
|
+
const appDir = findAppDir(root);
|
|
25
|
+
const nextConfig = findNextConfig(root);
|
|
26
|
+
const clientEnv = findClientEnvKeys(root);
|
|
27
|
+
|
|
28
|
+
if (!appDir) {
|
|
29
|
+
return {
|
|
30
|
+
root,
|
|
31
|
+
appDir: null,
|
|
32
|
+
nextConfig,
|
|
33
|
+
clientEnv,
|
|
34
|
+
pages: [],
|
|
35
|
+
layouts: [],
|
|
36
|
+
clients: [],
|
|
37
|
+
components: [],
|
|
38
|
+
blockers: [
|
|
39
|
+
{
|
|
40
|
+
kind: "no-app-dir",
|
|
41
|
+
message: "No app/ or src/app/ directory found (App Router required).",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
pagesRouter: fs.existsSync(path.join(root, "pages")),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const inv = inventoryApp(appDir);
|
|
49
|
+
/** @type {object[]} */
|
|
50
|
+
const pages = [];
|
|
51
|
+
/** @type {object[]} */
|
|
52
|
+
const clients = [];
|
|
53
|
+
/** @type {object[]} */
|
|
54
|
+
const components = [];
|
|
55
|
+
/** @type {object[]} */
|
|
56
|
+
const blockers = [];
|
|
57
|
+
|
|
58
|
+
if (fs.existsSync(path.join(root, "pages"))) {
|
|
59
|
+
blockers.push({
|
|
60
|
+
kind: "pages-router",
|
|
61
|
+
message: "pages/ directory present — Pages Router is out of scope for this codemod.",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const nestedLayouts = inv.layouts.filter((l) => l.depth > 0);
|
|
66
|
+
if (nestedLayouts.length) {
|
|
67
|
+
blockers.push({
|
|
68
|
+
kind: "nested-layouts",
|
|
69
|
+
message: `${nestedLayouts.length} nested layout(s); JSkelet has a single layout — flatten manually.`,
|
|
70
|
+
files: nestedLayouts.map((l) => l.rel),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const page of inv.pages) {
|
|
75
|
+
const code = fs.readFileSync(page.abs, "utf8");
|
|
76
|
+
const classification = classifySource(code, page.rel);
|
|
77
|
+
pages.push({ ...page, classification });
|
|
78
|
+
if (classification.hasServerAction) {
|
|
79
|
+
blockers.push({
|
|
80
|
+
kind: "server-action",
|
|
81
|
+
file: page.rel,
|
|
82
|
+
message: "Server Actions have no equivalent — use app.post handlers.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (classification.hasSuspense) {
|
|
86
|
+
blockers.push({
|
|
87
|
+
kind: "suspense",
|
|
88
|
+
file: page.rel,
|
|
89
|
+
message: "Suspense / streaming is not supported — use fragment endpoints.",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const layout of inv.layouts) {
|
|
95
|
+
const code = fs.readFileSync(layout.abs, "utf8");
|
|
96
|
+
layout.classification = classifySource(code, layout.rel);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const src of inv.sources) {
|
|
100
|
+
const code = fs.readFileSync(src.abs, "utf8");
|
|
101
|
+
const classification = classifySource(code, src.rel);
|
|
102
|
+
if (classification.kind === "client" || classification.isClient) {
|
|
103
|
+
clients.push({ ...src, classification });
|
|
104
|
+
} else if (classification.kind === "server-component") {
|
|
105
|
+
components.push({ ...src, classification });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const dir of ["components", "src/components", "lib/components"]) {
|
|
110
|
+
const abs = path.join(root, dir);
|
|
111
|
+
if (!fs.existsSync(abs)) continue;
|
|
112
|
+
walkFiles(abs, (_rel, fileAbs) => {
|
|
113
|
+
if (!/\.(jsx?|tsx?)$/.test(fileAbs)) return;
|
|
114
|
+
const fromRoot = path.relative(root, fileAbs).replace(/\\/g, "/");
|
|
115
|
+
const code = fs.readFileSync(fileAbs, "utf8");
|
|
116
|
+
const classification = classifySource(code, fromRoot);
|
|
117
|
+
if (classification.kind === "client" || classification.isClient) {
|
|
118
|
+
clients.push({ abs: fileAbs, rel: fromRoot, classification });
|
|
119
|
+
} else if (classification.kind === "server-component") {
|
|
120
|
+
components.push({ abs: fileAbs, rel: fromRoot, classification });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
root,
|
|
127
|
+
appDir: path.relative(root, appDir).replace(/\\/g, "/"),
|
|
128
|
+
nextConfig: nextConfig ? path.relative(root, nextConfig).replace(/\\/g, "/") : null,
|
|
129
|
+
clientEnv,
|
|
130
|
+
pages,
|
|
131
|
+
layouts: inv.layouts,
|
|
132
|
+
clients,
|
|
133
|
+
components,
|
|
134
|
+
blockers,
|
|
135
|
+
pagesRouter: fs.existsSync(path.join(root, "pages")),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* İnsan-okur özet.
|
|
141
|
+
* @param {ReturnType<typeof scanProject>} report
|
|
142
|
+
* @returns {string}
|
|
143
|
+
*/
|
|
144
|
+
export function formatScanReport(report) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
lines.push(`Next.js → JSkelet scan`);
|
|
147
|
+
lines.push(` app: ${report.appDir ?? "(missing)"}`);
|
|
148
|
+
lines.push(` config: ${report.nextConfig ?? "(none)"}`);
|
|
149
|
+
lines.push(` pages: ${report.pages.length}`);
|
|
150
|
+
lines.push(` layouts: ${report.layouts.length}`);
|
|
151
|
+
lines.push(` client modules: ${report.clients.length}`);
|
|
152
|
+
lines.push(` server components: ${report.components.length}`);
|
|
153
|
+
if (report.clientEnv.length) {
|
|
154
|
+
lines.push(` NEXT_PUBLIC_* → clientEnv candidates: ${report.clientEnv.join(", ")}`);
|
|
155
|
+
}
|
|
156
|
+
if (report.pages.length) {
|
|
157
|
+
lines.push("");
|
|
158
|
+
lines.push("Pages:");
|
|
159
|
+
for (const p of report.pages) {
|
|
160
|
+
const flags = [];
|
|
161
|
+
if (p.classification?.revalidate != null) flags.push(`revalidate=${p.classification.revalidate}`);
|
|
162
|
+
if (p.classification?.hasGenerateMetadata) flags.push("metadata");
|
|
163
|
+
if (p.classification?.hasGenerateStaticParams) flags.push("staticParams");
|
|
164
|
+
lines.push(` ${p.url} ← ${p.rel}${flags.length ? ` (${flags.join(", ")})` : ""}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (report.blockers.length) {
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push("Blockers:");
|
|
170
|
+
for (const b of report.blockers) {
|
|
171
|
+
lines.push(` ⚠ ${b.message}${b.file ? ` [${b.file}]` : ""}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
lines.push("");
|
|
175
|
+
lines.push("Next: jskelet migrate apply [--write] | jskelet migrate config [--write]");
|
|
176
|
+
return lines.join("\n");
|
|
177
|
+
}
|