@jskit-ai/jskit-cli 0.2.81 → 0.2.83
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 +6 -4
- package/src/server/appBlueprint.js +1 -1
- package/src/server/commandHandlers/helperMap.js +104 -0
- package/src/server/commandHandlers/session.js +127 -3
- package/src/server/commandHandlers/show.js +169 -34
- package/src/server/core/argParser.js +8 -0
- package/src/server/core/commandCatalog.js +60 -2
- package/src/server/core/createCommandHandlers.js +4 -1
- package/src/server/helperMap.js +463 -0
- package/src/server/helperMapPaths.js +7 -0
- package/src/server/sessionRuntime/appReadiness.js +55 -0
- package/src/server/sessionRuntime/constants.js +326 -87
- package/src/server/sessionRuntime/preconditions.js +382 -5
- package/src/server/sessionRuntime/promptRenderer.js +15 -2
- package/src/server/sessionRuntime/prompts/automated_checks.md +42 -0
- package/src/server/sessionRuntime/prompts/deep_ui_check.md +53 -0
- package/src/server/sessionRuntime/prompts/execute_plan.md +33 -7
- package/src/server/sessionRuntime/prompts/final_comment.md +3 -1
- package/src/server/sessionRuntime/prompts/issue_details.md +46 -0
- package/src/server/sessionRuntime/prompts/new_issue.md +15 -2
- package/src/server/sessionRuntime/prompts/plan_issue.md +40 -9
- package/src/server/sessionRuntime/prompts/resolve_deslop_findings.md +16 -0
- package/src/server/sessionRuntime/prompts/review_changes.md +46 -5
- package/src/server/sessionRuntime/prompts/update_blueprint.md +38 -0
- package/src/server/sessionRuntime/prompts/user_check.md +15 -1
- package/src/server/sessionRuntime/responses.js +860 -62
- package/src/server/sessionRuntime.js +1699 -140
- package/src/server/sessionRuntime/prompts/doctor_failure.md +0 -26
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdir,
|
|
3
|
+
readFile,
|
|
4
|
+
readdir,
|
|
5
|
+
stat,
|
|
6
|
+
writeFile
|
|
7
|
+
} from "node:fs/promises";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { compileScript, parse as parseVueSfc } from "@vue/compiler-sfc";
|
|
10
|
+
import tsMorph from "ts-morph";
|
|
11
|
+
import {
|
|
12
|
+
HELPER_MAP_JSON_RELATIVE_PATH,
|
|
13
|
+
HELPER_MAP_MARKDOWN_RELATIVE_PATH
|
|
14
|
+
} from "./helperMapPaths.js";
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
ModuleKind,
|
|
18
|
+
ModuleResolutionKind,
|
|
19
|
+
Project,
|
|
20
|
+
ScriptTarget
|
|
21
|
+
} = tsMorph;
|
|
22
|
+
|
|
23
|
+
const HELPER_MAP_SCHEMA_VERSION = 1;
|
|
24
|
+
const CODE_EXTENSIONS = new Set([".cjs", ".js", ".jsx", ".mjs", ".ts", ".tsx", ".vue"]);
|
|
25
|
+
const APP_SCAN_ROOTS = Object.freeze(["src", "packages", "config", "server", "scripts"]);
|
|
26
|
+
const EXCLUDED_DIR_NAMES = new Set([
|
|
27
|
+
".git",
|
|
28
|
+
".jskit",
|
|
29
|
+
".npm-cache",
|
|
30
|
+
"coverage",
|
|
31
|
+
"dist",
|
|
32
|
+
"node_modules"
|
|
33
|
+
]);
|
|
34
|
+
const HELPER_NAME_PATTERN =
|
|
35
|
+
/^(assert|build|coerce|create|ensure|extract|format|get|has|is|list|load|make|map|normalize|parse|read|render|resolve|run|serialize|to|update|use|validate|write)[A-Z_]/u;
|
|
36
|
+
|
|
37
|
+
async function pathExists(filePath) {
|
|
38
|
+
try {
|
|
39
|
+
await stat(filePath);
|
|
40
|
+
return true;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error && error.code === "ENOENT") {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function readJsonFile(filePath) {
|
|
50
|
+
return JSON.parse(await readFile(filePath, "utf8"));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizePackageDependencies(packageJson = {}) {
|
|
54
|
+
const dependencies = {
|
|
55
|
+
...(packageJson.dependencies || {}),
|
|
56
|
+
...(packageJson.devDependencies || {}),
|
|
57
|
+
...(packageJson.peerDependencies || {}),
|
|
58
|
+
...(packageJson.optionalDependencies || {})
|
|
59
|
+
};
|
|
60
|
+
return Object.keys(dependencies).sort((left, right) => left.localeCompare(right));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function classifySymbol(name = "") {
|
|
64
|
+
if (!name || name === "default") {
|
|
65
|
+
return "default";
|
|
66
|
+
}
|
|
67
|
+
if (/^use[A-Z]/u.test(name)) {
|
|
68
|
+
return "composable";
|
|
69
|
+
}
|
|
70
|
+
if (/^[A-Z]/u.test(name)) {
|
|
71
|
+
return "component_or_class";
|
|
72
|
+
}
|
|
73
|
+
if (HELPER_NAME_PATTERN.test(name)) {
|
|
74
|
+
return "helper";
|
|
75
|
+
}
|
|
76
|
+
return "export";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function createExportAnalysisProject() {
|
|
80
|
+
return new Project({
|
|
81
|
+
skipAddingFilesFromTsConfig: true,
|
|
82
|
+
compilerOptions: {
|
|
83
|
+
allowJs: true,
|
|
84
|
+
checkJs: false,
|
|
85
|
+
module: ModuleKind.ESNext,
|
|
86
|
+
moduleResolution: ModuleResolutionKind.NodeNext,
|
|
87
|
+
target: ScriptTarget.ESNext
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function kindFromDeclaration(declaration, exportName = "") {
|
|
93
|
+
if (exportName === "default") {
|
|
94
|
+
return "default";
|
|
95
|
+
}
|
|
96
|
+
switch (declaration.getKindName()) {
|
|
97
|
+
case "ClassDeclaration":
|
|
98
|
+
return "class";
|
|
99
|
+
case "EnumDeclaration":
|
|
100
|
+
return "enum";
|
|
101
|
+
case "FunctionDeclaration":
|
|
102
|
+
case "FunctionExpression":
|
|
103
|
+
case "MethodDeclaration":
|
|
104
|
+
return "function";
|
|
105
|
+
case "InterfaceDeclaration":
|
|
106
|
+
return "interface";
|
|
107
|
+
case "TypeAliasDeclaration":
|
|
108
|
+
return "type";
|
|
109
|
+
case "VariableDeclaration": {
|
|
110
|
+
const initializer = typeof declaration.getInitializer === "function"
|
|
111
|
+
? declaration.getInitializer()
|
|
112
|
+
: null;
|
|
113
|
+
const initializerKind = initializer?.getKindName?.() || "";
|
|
114
|
+
return initializerKind === "ArrowFunction" || initializerKind === "FunctionExpression"
|
|
115
|
+
? "function"
|
|
116
|
+
: "value";
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
return "export";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function addSymbol(symbols, symbol) {
|
|
124
|
+
if (!symbol.name) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const key = `${symbol.name}:${symbol.kind}`;
|
|
128
|
+
if (symbols.has(key)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
symbols.set(key, {
|
|
132
|
+
name: symbol.name,
|
|
133
|
+
kind: symbol.kind,
|
|
134
|
+
role: classifySymbol(symbol.name)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractExportedSymbols(sourceFile) {
|
|
139
|
+
const symbols = new Map();
|
|
140
|
+
for (const [name, declarations] of sourceFile.getExportedDeclarations()) {
|
|
141
|
+
for (const declaration of declarations) {
|
|
142
|
+
addSymbol(symbols, {
|
|
143
|
+
name,
|
|
144
|
+
kind: kindFromDeclaration(declaration, name)
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return [...symbols.values()].sort((left, right) => {
|
|
150
|
+
const byName = left.name.localeCompare(right.name);
|
|
151
|
+
return byName || left.kind.localeCompare(right.kind);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function extractVueScriptSource(source = "", filePath = "") {
|
|
156
|
+
const parsed = parseVueSfc(source, {
|
|
157
|
+
filename: filePath
|
|
158
|
+
});
|
|
159
|
+
if (parsed.errors.length > 0) {
|
|
160
|
+
throw new Error(parsed.errors.map((error) => error.message || String(error)).join("; "));
|
|
161
|
+
}
|
|
162
|
+
const descriptor = parsed.descriptor;
|
|
163
|
+
if (descriptor.scriptSetup) {
|
|
164
|
+
return compileScript(descriptor, {
|
|
165
|
+
id: filePath
|
|
166
|
+
}).content;
|
|
167
|
+
}
|
|
168
|
+
if (descriptor.script) {
|
|
169
|
+
return descriptor.script.content;
|
|
170
|
+
}
|
|
171
|
+
return "";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function addCodeFileToProject(project, file) {
|
|
175
|
+
if (path.extname(file.absolutePath) !== ".vue") {
|
|
176
|
+
return project.addSourceFileAtPath(file.absolutePath);
|
|
177
|
+
}
|
|
178
|
+
const source = extractVueScriptSource(await readFile(file.absolutePath, "utf8"), file.absolutePath);
|
|
179
|
+
if (!source.trim()) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return project.createSourceFile(`${file.absolutePath}.ts`, source, {
|
|
183
|
+
overwrite: true
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function walkCodeFiles(rootPath, relativeRoot = "") {
|
|
188
|
+
const entries = await readdir(rootPath, {
|
|
189
|
+
withFileTypes: true
|
|
190
|
+
});
|
|
191
|
+
const files = [];
|
|
192
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
193
|
+
if (EXCLUDED_DIR_NAMES.has(entry.name)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const absolutePath = path.join(rootPath, entry.name);
|
|
197
|
+
const relativePath = path.join(relativeRoot, entry.name).split(path.sep).join("/");
|
|
198
|
+
if (entry.isDirectory()) {
|
|
199
|
+
files.push(...await walkCodeFiles(absolutePath, relativePath));
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (!entry.isFile() || !CODE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
files.push({
|
|
206
|
+
absolutePath,
|
|
207
|
+
relativePath
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return files;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function collectAppExports(targetRoot) {
|
|
214
|
+
const scanFiles = [];
|
|
215
|
+
for (const scanRoot of APP_SCAN_ROOTS) {
|
|
216
|
+
const rootPath = path.join(targetRoot, scanRoot);
|
|
217
|
+
if (await pathExists(rootPath)) {
|
|
218
|
+
scanFiles.push(...await walkCodeFiles(rootPath, scanRoot));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const project = createExportAnalysisProject();
|
|
223
|
+
const files = [];
|
|
224
|
+
for (const file of scanFiles.sort((left, right) => left.relativePath.localeCompare(right.relativePath))) {
|
|
225
|
+
const sourceFile = await addCodeFileToProject(project, file);
|
|
226
|
+
if (!sourceFile) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const symbols = extractExportedSymbols(sourceFile);
|
|
230
|
+
if (symbols.length === 0) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
files.push({
|
|
234
|
+
path: file.relativePath,
|
|
235
|
+
exports: symbols
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return files;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function flattenPackageExports(exportsField) {
|
|
242
|
+
const targets = new Map();
|
|
243
|
+
|
|
244
|
+
function addTarget(subpath, target) {
|
|
245
|
+
if (!target || target.includes("*")) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
targets.set(`${subpath}:${target}`, {
|
|
249
|
+
subpath,
|
|
250
|
+
target
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function collect(value, subpath = ".") {
|
|
255
|
+
if (typeof value === "string") {
|
|
256
|
+
addTarget(subpath, value);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (!value || Array.isArray(value) || typeof value !== "object") {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const entries = Object.entries(value);
|
|
263
|
+
const hasSubpathKeys = entries.some(([key]) => key.startsWith("."));
|
|
264
|
+
for (const [key, nested] of entries) {
|
|
265
|
+
if (key.startsWith(".")) {
|
|
266
|
+
collect(nested, key);
|
|
267
|
+
} else {
|
|
268
|
+
collect(nested, hasSubpathKeys ? subpath : subpath || ".");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
collect(exportsField, ".");
|
|
274
|
+
return [...targets.values()].sort((left, right) => {
|
|
275
|
+
const bySubpath = left.subpath.localeCompare(right.subpath);
|
|
276
|
+
return bySubpath || left.target.localeCompare(right.target);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function collectJskitPackageExports(targetRoot, packageJson = {}) {
|
|
281
|
+
const packageNames = normalizePackageDependencies(packageJson)
|
|
282
|
+
.filter((name) => name.startsWith("@jskit-ai/"));
|
|
283
|
+
const packages = [];
|
|
284
|
+
const project = createExportAnalysisProject();
|
|
285
|
+
|
|
286
|
+
for (const packageName of packageNames) {
|
|
287
|
+
const packageRoot = path.join(targetRoot, "node_modules", ...packageName.split("/"));
|
|
288
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
289
|
+
if (!await pathExists(packageJsonPath)) {
|
|
290
|
+
packages.push({
|
|
291
|
+
name: packageName,
|
|
292
|
+
installed: false,
|
|
293
|
+
exports: []
|
|
294
|
+
});
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const installedPackageJson = await readJsonFile(packageJsonPath);
|
|
299
|
+
const exportTargets = flattenPackageExports(installedPackageJson.exports || {});
|
|
300
|
+
const exports = [];
|
|
301
|
+
for (const exportTarget of exportTargets) {
|
|
302
|
+
const normalizedTarget = exportTarget.target.replace(/^\.\//u, "");
|
|
303
|
+
const targetPath = path.join(packageRoot, normalizedTarget);
|
|
304
|
+
const ext = path.extname(targetPath);
|
|
305
|
+
if (!CODE_EXTENSIONS.has(ext) || !await pathExists(targetPath)) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const sourceFile = await addCodeFileToProject(project, {
|
|
309
|
+
absolutePath: targetPath,
|
|
310
|
+
relativePath: normalizedTarget
|
|
311
|
+
});
|
|
312
|
+
exports.push({
|
|
313
|
+
subpath: exportTarget.subpath,
|
|
314
|
+
target: normalizedTarget.split(path.sep).join("/"),
|
|
315
|
+
exports: sourceFile ? extractExportedSymbols(sourceFile) : []
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
packages.push({
|
|
320
|
+
name: packageName,
|
|
321
|
+
version: installedPackageJson.version || "",
|
|
322
|
+
installed: true,
|
|
323
|
+
exports
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return packages;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function renderExportList(symbols = []) {
|
|
331
|
+
if (symbols.length === 0) {
|
|
332
|
+
return " - no exported symbols detected";
|
|
333
|
+
}
|
|
334
|
+
return symbols
|
|
335
|
+
.map((symbol) => ` - ${symbol.name} (${symbol.kind}, ${symbol.role})`)
|
|
336
|
+
.join("\n");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function renderHelperMapMarkdown(map) {
|
|
340
|
+
const lines = [
|
|
341
|
+
"# JSKIT Helper Map",
|
|
342
|
+
"",
|
|
343
|
+
"Generated by `jskit helper-map update`. Read this before adding new helpers, composables, service functions, maps, or package glue.",
|
|
344
|
+
"",
|
|
345
|
+
`Root package: ${map.rootPackage.name || "unknown"}`,
|
|
346
|
+
"",
|
|
347
|
+
"## App-local exports",
|
|
348
|
+
""
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
if (map.app.files.length === 0) {
|
|
352
|
+
lines.push("No app-local exported helpers or symbols detected.", "");
|
|
353
|
+
} else {
|
|
354
|
+
for (const file of map.app.files) {
|
|
355
|
+
lines.push(`- ${file.path}`, renderExportList(file.exports), "");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
lines.push("## Direct JSKIT package exports", "");
|
|
360
|
+
if (map.jskitPackages.length === 0) {
|
|
361
|
+
lines.push("No direct `@jskit-ai/*` dependencies were found.", "");
|
|
362
|
+
} else {
|
|
363
|
+
for (const packageEntry of map.jskitPackages) {
|
|
364
|
+
if (!packageEntry.installed) {
|
|
365
|
+
lines.push(`- ${packageEntry.name}: not installed in node_modules`, "");
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
lines.push(`- ${packageEntry.name}@${packageEntry.version || "unknown"}`);
|
|
369
|
+
if (packageEntry.exports.length === 0) {
|
|
370
|
+
lines.push(" - no exported code files detected");
|
|
371
|
+
} else {
|
|
372
|
+
for (const exportEntry of packageEntry.exports) {
|
|
373
|
+
lines.push(` - ${exportEntry.subpath} -> ${exportEntry.target}`);
|
|
374
|
+
for (const symbol of exportEntry.exports) {
|
|
375
|
+
lines.push(` - ${symbol.name} (${symbol.kind}, ${symbol.role})`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
lines.push("");
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return `${lines.join("\n").replace(/\n{3,}/gu, "\n\n").trimEnd()}\n`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function buildHelperMap({ targetRoot }) {
|
|
387
|
+
const packageJsonPath = path.join(targetRoot, "package.json");
|
|
388
|
+
const packageJson = await readJsonFile(packageJsonPath);
|
|
389
|
+
const map = {
|
|
390
|
+
schemaVersion: HELPER_MAP_SCHEMA_VERSION,
|
|
391
|
+
generatedBy: "jskit helper-map update",
|
|
392
|
+
rootPackage: {
|
|
393
|
+
name: packageJson.name || "",
|
|
394
|
+
version: packageJson.version || ""
|
|
395
|
+
},
|
|
396
|
+
app: {
|
|
397
|
+
files: await collectAppExports(targetRoot)
|
|
398
|
+
},
|
|
399
|
+
jskitPackages: await collectJskitPackageExports(targetRoot, packageJson)
|
|
400
|
+
};
|
|
401
|
+
return {
|
|
402
|
+
ok: true,
|
|
403
|
+
map,
|
|
404
|
+
helperMapJsonPath: path.join(targetRoot, HELPER_MAP_JSON_RELATIVE_PATH),
|
|
405
|
+
helperMapMarkdownPath: path.join(targetRoot, HELPER_MAP_MARKDOWN_RELATIVE_PATH)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function readHelperMap({ targetRoot }) {
|
|
410
|
+
const helperMapJsonPath = path.join(targetRoot, HELPER_MAP_JSON_RELATIVE_PATH);
|
|
411
|
+
const helperMapMarkdownPath = path.join(targetRoot, HELPER_MAP_MARKDOWN_RELATIVE_PATH);
|
|
412
|
+
if (!await pathExists(helperMapJsonPath)) {
|
|
413
|
+
return {
|
|
414
|
+
ok: true,
|
|
415
|
+
exists: false,
|
|
416
|
+
helperMapJsonPath,
|
|
417
|
+
helperMapMarkdownPath,
|
|
418
|
+
map: null,
|
|
419
|
+
markdown: ""
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
ok: true,
|
|
424
|
+
exists: true,
|
|
425
|
+
helperMapJsonPath,
|
|
426
|
+
helperMapMarkdownPath,
|
|
427
|
+
map: await readJsonFile(helperMapJsonPath),
|
|
428
|
+
markdown: await pathExists(helperMapMarkdownPath) ? await readFile(helperMapMarkdownPath, "utf8") : ""
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function updateHelperMap({ targetRoot }) {
|
|
433
|
+
const payload = await buildHelperMap({ targetRoot });
|
|
434
|
+
const markdown = renderHelperMapMarkdown(payload.map);
|
|
435
|
+
const json = `${JSON.stringify(payload.map, null, 2)}\n`;
|
|
436
|
+
const currentJson = await pathExists(payload.helperMapJsonPath)
|
|
437
|
+
? await readFile(payload.helperMapJsonPath, "utf8")
|
|
438
|
+
: "";
|
|
439
|
+
const currentMarkdown = await pathExists(payload.helperMapMarkdownPath)
|
|
440
|
+
? await readFile(payload.helperMapMarkdownPath, "utf8")
|
|
441
|
+
: "";
|
|
442
|
+
const changed = currentJson !== json || currentMarkdown !== markdown;
|
|
443
|
+
if (changed) {
|
|
444
|
+
await mkdir(path.dirname(payload.helperMapJsonPath), {
|
|
445
|
+
recursive: true
|
|
446
|
+
});
|
|
447
|
+
await writeFile(payload.helperMapJsonPath, json, "utf8");
|
|
448
|
+
await writeFile(payload.helperMapMarkdownPath, markdown, "utf8");
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
...payload,
|
|
452
|
+
changed,
|
|
453
|
+
markdown
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export {
|
|
458
|
+
HELPER_MAP_JSON_RELATIVE_PATH,
|
|
459
|
+
HELPER_MAP_MARKDOWN_RELATIVE_PATH,
|
|
460
|
+
buildHelperMap,
|
|
461
|
+
readHelperMap,
|
|
462
|
+
updateHelperMap
|
|
463
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { access, stat } from "node:fs/promises";
|
|
2
|
+
import { constants as fsConstants } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const REQUIRED_READY_FILES = Object.freeze([
|
|
6
|
+
"package.json",
|
|
7
|
+
".jskit/lock.json",
|
|
8
|
+
"config/public.js"
|
|
9
|
+
]);
|
|
10
|
+
const REQUIRED_READY_DIRECTORIES = Object.freeze([
|
|
11
|
+
"src",
|
|
12
|
+
"packages"
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
async function pathExists(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
await access(filePath, fsConstants.F_OK);
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function directoryExists(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
return (await stat(filePath)).isDirectory();
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function inspectReadyJskitAppRoot(rootPath) {
|
|
33
|
+
const missing = [];
|
|
34
|
+
for (const relativePath of REQUIRED_READY_FILES) {
|
|
35
|
+
if (!await pathExists(path.join(rootPath, relativePath))) {
|
|
36
|
+
missing.push(relativePath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const relativePath of REQUIRED_READY_DIRECTORIES) {
|
|
40
|
+
if (!await directoryExists(path.join(rootPath, relativePath))) {
|
|
41
|
+
missing.push(`${relativePath}/`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
missing,
|
|
46
|
+
ok: missing.length < 1,
|
|
47
|
+
requiredDirectories: [...REQUIRED_READY_DIRECTORIES],
|
|
48
|
+
requiredFiles: [...REQUIRED_READY_FILES],
|
|
49
|
+
rootPath
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
inspectReadyJskitAppRoot
|
|
55
|
+
};
|