@jskit-ai/jskit-cli 0.2.92 → 0.2.96
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 +1 -1
- package/src/server/helperMap.js +214 -56
package/package.json
CHANGED
package/src/server/helperMap.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHash
|
|
3
|
+
} from "node:crypto";
|
|
1
4
|
import {
|
|
2
5
|
mkdir,
|
|
3
6
|
readFile,
|
|
@@ -17,10 +20,11 @@ const {
|
|
|
17
20
|
ModuleKind,
|
|
18
21
|
ModuleResolutionKind,
|
|
19
22
|
Project,
|
|
20
|
-
ScriptTarget
|
|
23
|
+
ScriptTarget,
|
|
24
|
+
ts
|
|
21
25
|
} = tsMorph;
|
|
22
26
|
|
|
23
|
-
const HELPER_MAP_SCHEMA_VERSION =
|
|
27
|
+
const HELPER_MAP_SCHEMA_VERSION = 2;
|
|
24
28
|
const CODE_EXTENSIONS = new Set([".cjs", ".js", ".jsx", ".mjs", ".ts", ".tsx", ".vue"]);
|
|
25
29
|
const APP_SCAN_ROOTS = Object.freeze(["src", "packages", "config", "server", "scripts"]);
|
|
26
30
|
const EXCLUDED_DIR_NAMES = new Set([
|
|
@@ -50,6 +54,12 @@ async function readJsonFile(filePath) {
|
|
|
50
54
|
return JSON.parse(await readFile(filePath, "utf8"));
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
async function readFileHash(filePath) {
|
|
58
|
+
return createHash("sha256")
|
|
59
|
+
.update(await readFile(filePath))
|
|
60
|
+
.digest("hex");
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
function normalizePackageDependencies(packageJson = {}) {
|
|
54
64
|
const dependencies = {
|
|
55
65
|
...(packageJson.dependencies || {}),
|
|
@@ -89,37 +99,6 @@ function createExportAnalysisProject() {
|
|
|
89
99
|
});
|
|
90
100
|
}
|
|
91
101
|
|
|
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
102
|
function addSymbol(symbols, symbol) {
|
|
124
103
|
if (!symbol.name) {
|
|
125
104
|
return;
|
|
@@ -135,14 +114,116 @@ function addSymbol(symbols, symbol) {
|
|
|
135
114
|
});
|
|
136
115
|
}
|
|
137
116
|
|
|
117
|
+
function compilerNodeHasModifier(node, modifierKind) {
|
|
118
|
+
return Array.isArray(node?.modifiers) && node.modifiers.some((modifier) => modifier.kind === modifierKind);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function exportedDeclarationName(node) {
|
|
122
|
+
if (compilerNodeHasModifier(node, ts.SyntaxKind.DefaultKeyword)) {
|
|
123
|
+
return "default";
|
|
124
|
+
}
|
|
125
|
+
return String(node?.name?.text || node?.name?.escapedText || "").trim();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function addExportedDeclarationSymbol(symbols, node, kind = "export") {
|
|
129
|
+
const name = exportedDeclarationName(node);
|
|
130
|
+
if (!name) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
addSymbol(symbols, {
|
|
134
|
+
kind: name === "default" ? "default" : kind,
|
|
135
|
+
name
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function bindingNameTexts(bindingName, names = []) {
|
|
140
|
+
if (!bindingName) {
|
|
141
|
+
return names;
|
|
142
|
+
}
|
|
143
|
+
if (ts.isIdentifier(bindingName)) {
|
|
144
|
+
names.push(String(bindingName.text || ""));
|
|
145
|
+
return names;
|
|
146
|
+
}
|
|
147
|
+
if (ts.isObjectBindingPattern(bindingName) || ts.isArrayBindingPattern(bindingName)) {
|
|
148
|
+
for (const element of bindingName.elements || []) {
|
|
149
|
+
if (element.name) {
|
|
150
|
+
bindingNameTexts(element.name, names);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return names;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function addVariableStatementExports(symbols, statement) {
|
|
158
|
+
if (!compilerNodeHasModifier(statement, ts.SyntaxKind.ExportKeyword)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for (const declaration of statement.declarationList?.declarations || []) {
|
|
162
|
+
for (const name of bindingNameTexts(declaration.name)) {
|
|
163
|
+
addSymbol(symbols, {
|
|
164
|
+
kind: declaration.initializer &&
|
|
165
|
+
(ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer))
|
|
166
|
+
? "function"
|
|
167
|
+
: "value",
|
|
168
|
+
name
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function addNamedExportSymbols(symbols, exportClause) {
|
|
175
|
+
if (!exportClause) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (ts.isNamespaceExport(exportClause)) {
|
|
179
|
+
addSymbol(symbols, {
|
|
180
|
+
kind: "export",
|
|
181
|
+
name: String(exportClause.name?.text || "")
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!ts.isNamedExports(exportClause)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const specifier of exportClause.elements || []) {
|
|
189
|
+
addSymbol(symbols, {
|
|
190
|
+
kind: "export",
|
|
191
|
+
name: String(specifier.name?.text || "")
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
138
196
|
function extractExportedSymbols(sourceFile) {
|
|
139
197
|
const symbols = new Map();
|
|
140
|
-
for (const
|
|
141
|
-
|
|
198
|
+
for (const statement of sourceFile.compilerNode.statements || []) {
|
|
199
|
+
if (ts.isExportDeclaration(statement)) {
|
|
200
|
+
addNamedExportSymbols(symbols, statement.exportClause);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (ts.isExportAssignment(statement)) {
|
|
142
204
|
addSymbol(symbols, {
|
|
143
|
-
|
|
144
|
-
|
|
205
|
+
kind: "default",
|
|
206
|
+
name: "default"
|
|
145
207
|
});
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (ts.isVariableStatement(statement)) {
|
|
211
|
+
addVariableStatementExports(symbols, statement);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!compilerNodeHasModifier(statement, ts.SyntaxKind.ExportKeyword)) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (ts.isFunctionDeclaration(statement)) {
|
|
218
|
+
addExportedDeclarationSymbol(symbols, statement, "function");
|
|
219
|
+
} else if (ts.isClassDeclaration(statement)) {
|
|
220
|
+
addExportedDeclarationSymbol(symbols, statement, "class");
|
|
221
|
+
} else if (ts.isInterfaceDeclaration(statement)) {
|
|
222
|
+
addExportedDeclarationSymbol(symbols, statement, "interface");
|
|
223
|
+
} else if (ts.isTypeAliasDeclaration(statement)) {
|
|
224
|
+
addExportedDeclarationSymbol(symbols, statement, "type");
|
|
225
|
+
} else if (ts.isEnumDeclaration(statement)) {
|
|
226
|
+
addExportedDeclarationSymbol(symbols, statement, "enum");
|
|
146
227
|
}
|
|
147
228
|
}
|
|
148
229
|
|
|
@@ -277,11 +358,75 @@ function flattenPackageExports(exportsField) {
|
|
|
277
358
|
});
|
|
278
359
|
}
|
|
279
360
|
|
|
280
|
-
async function
|
|
361
|
+
async function packageExportTargetRecords(packageRoot, exportTargets = []) {
|
|
362
|
+
const records = [];
|
|
363
|
+
for (const exportTarget of exportTargets) {
|
|
364
|
+
const normalizedTarget = exportTarget.target.replace(/^\.\//u, "");
|
|
365
|
+
const targetPath = path.join(packageRoot, normalizedTarget);
|
|
366
|
+
const ext = path.extname(targetPath);
|
|
367
|
+
if (!CODE_EXTENSIONS.has(ext) || !await pathExists(targetPath)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
records.push({
|
|
371
|
+
absolutePath: targetPath,
|
|
372
|
+
hash: await readFileHash(targetPath),
|
|
373
|
+
subpath: exportTarget.subpath,
|
|
374
|
+
target: normalizedTarget.split(path.sep).join("/")
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
return records;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function packageExportFingerprint({
|
|
381
|
+
installedPackageJson = {},
|
|
382
|
+
packageName = "",
|
|
383
|
+
targetRecords = []
|
|
384
|
+
} = {}) {
|
|
385
|
+
return createHash("sha256")
|
|
386
|
+
.update(JSON.stringify({
|
|
387
|
+
name: packageName,
|
|
388
|
+
version: installedPackageJson.version || "",
|
|
389
|
+
targets: targetRecords.map((record) => ({
|
|
390
|
+
hash: record.hash,
|
|
391
|
+
subpath: record.subpath,
|
|
392
|
+
target: record.target
|
|
393
|
+
}))
|
|
394
|
+
}))
|
|
395
|
+
.digest("hex");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function cachedPackageExports(previousPackagesByName = new Map(), packageName = "", fingerprint = "") {
|
|
399
|
+
const previous = previousPackagesByName.get(packageName);
|
|
400
|
+
if (
|
|
401
|
+
!previous ||
|
|
402
|
+
previous.installed !== true ||
|
|
403
|
+
previous.fingerprint !== fingerprint ||
|
|
404
|
+
!Array.isArray(previous.exports)
|
|
405
|
+
) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
name: previous.name,
|
|
410
|
+
version: previous.version || "",
|
|
411
|
+
fingerprint: previous.fingerprint,
|
|
412
|
+
installed: true,
|
|
413
|
+
exports: previous.exports
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function previousPackageMap(previousMap = null) {
|
|
418
|
+
return new Map(
|
|
419
|
+
(Array.isArray(previousMap?.jskitPackages) ? previousMap.jskitPackages : [])
|
|
420
|
+
.map((packageEntry) => [packageEntry.name, packageEntry])
|
|
421
|
+
.filter(([name]) => name)
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function collectJskitPackageExports(targetRoot, packageJson = {}, previousMap = null) {
|
|
281
426
|
const packageNames = normalizePackageDependencies(packageJson)
|
|
282
427
|
.filter((name) => name.startsWith("@jskit-ai/"));
|
|
283
428
|
const packages = [];
|
|
284
|
-
const
|
|
429
|
+
const previousPackagesByName = previousPackageMap(previousMap);
|
|
285
430
|
|
|
286
431
|
for (const packageName of packageNames) {
|
|
287
432
|
const packageRoot = path.join(targetRoot, "node_modules", ...packageName.split("/"));
|
|
@@ -297,21 +442,28 @@ async function collectJskitPackageExports(targetRoot, packageJson = {}) {
|
|
|
297
442
|
|
|
298
443
|
const installedPackageJson = await readJsonFile(packageJsonPath);
|
|
299
444
|
const exportTargets = flattenPackageExports(installedPackageJson.exports || {});
|
|
445
|
+
const targetRecords = await packageExportTargetRecords(packageRoot, exportTargets);
|
|
446
|
+
const fingerprint = packageExportFingerprint({
|
|
447
|
+
installedPackageJson,
|
|
448
|
+
packageName,
|
|
449
|
+
targetRecords
|
|
450
|
+
});
|
|
451
|
+
const cachedEntry = cachedPackageExports(previousPackagesByName, packageName, fingerprint);
|
|
452
|
+
if (cachedEntry) {
|
|
453
|
+
packages.push(cachedEntry);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const project = createExportAnalysisProject();
|
|
300
458
|
const exports = [];
|
|
301
|
-
for (const
|
|
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
|
-
}
|
|
459
|
+
for (const targetRecord of targetRecords) {
|
|
308
460
|
const sourceFile = await addCodeFileToProject(project, {
|
|
309
|
-
absolutePath:
|
|
310
|
-
relativePath:
|
|
461
|
+
absolutePath: targetRecord.absolutePath,
|
|
462
|
+
relativePath: targetRecord.target
|
|
311
463
|
});
|
|
312
464
|
exports.push({
|
|
313
|
-
subpath:
|
|
314
|
-
target:
|
|
465
|
+
subpath: targetRecord.subpath,
|
|
466
|
+
target: targetRecord.target,
|
|
315
467
|
exports: sourceFile ? extractExportedSymbols(sourceFile) : []
|
|
316
468
|
});
|
|
317
469
|
}
|
|
@@ -319,6 +471,7 @@ async function collectJskitPackageExports(targetRoot, packageJson = {}) {
|
|
|
319
471
|
packages.push({
|
|
320
472
|
name: packageName,
|
|
321
473
|
version: installedPackageJson.version || "",
|
|
474
|
+
fingerprint,
|
|
322
475
|
installed: true,
|
|
323
476
|
exports
|
|
324
477
|
});
|
|
@@ -383,7 +536,7 @@ function renderHelperMapMarkdown(map) {
|
|
|
383
536
|
return `${lines.join("\n").replace(/\n{3,}/gu, "\n\n").trimEnd()}\n`;
|
|
384
537
|
}
|
|
385
538
|
|
|
386
|
-
async function buildHelperMap({ targetRoot }) {
|
|
539
|
+
async function buildHelperMap({ targetRoot, previousMap = null }) {
|
|
387
540
|
const packageJsonPath = path.join(targetRoot, "package.json");
|
|
388
541
|
const packageJson = await readJsonFile(packageJsonPath);
|
|
389
542
|
const map = {
|
|
@@ -396,7 +549,7 @@ async function buildHelperMap({ targetRoot }) {
|
|
|
396
549
|
app: {
|
|
397
550
|
files: await collectAppExports(targetRoot)
|
|
398
551
|
},
|
|
399
|
-
jskitPackages: await collectJskitPackageExports(targetRoot, packageJson)
|
|
552
|
+
jskitPackages: await collectJskitPackageExports(targetRoot, packageJson, previousMap)
|
|
400
553
|
};
|
|
401
554
|
return {
|
|
402
555
|
ok: true,
|
|
@@ -430,12 +583,17 @@ async function readHelperMap({ targetRoot }) {
|
|
|
430
583
|
}
|
|
431
584
|
|
|
432
585
|
async function updateHelperMap({ targetRoot }) {
|
|
433
|
-
const
|
|
586
|
+
const helperMapJsonPath = path.join(targetRoot, HELPER_MAP_JSON_RELATIVE_PATH);
|
|
587
|
+
const currentJson = await pathExists(helperMapJsonPath)
|
|
588
|
+
? await readFile(helperMapJsonPath, "utf8")
|
|
589
|
+
: "";
|
|
590
|
+
const previousMap = currentJson ? JSON.parse(currentJson) : null;
|
|
591
|
+
const payload = await buildHelperMap({
|
|
592
|
+
previousMap,
|
|
593
|
+
targetRoot
|
|
594
|
+
});
|
|
434
595
|
const markdown = renderHelperMapMarkdown(payload.map);
|
|
435
596
|
const json = `${JSON.stringify(payload.map, null, 2)}\n`;
|
|
436
|
-
const currentJson = await pathExists(payload.helperMapJsonPath)
|
|
437
|
-
? await readFile(payload.helperMapJsonPath, "utf8")
|
|
438
|
-
: "";
|
|
439
597
|
const currentMarkdown = await pathExists(payload.helperMapMarkdownPath)
|
|
440
598
|
? await readFile(payload.helperMapMarkdownPath, "utf8")
|
|
441
599
|
: "";
|