@vercel/python-analysis 0.11.1 → 0.13.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/dist/index.cjs +245 -4
- package/dist/index.d.ts +5 -3
- package/dist/index.js +249 -13
- package/dist/manifest/pyproject/schema.d.ts +13 -1
- package/dist/manifest/pyproject/schema.zod.d.ts +159 -0
- package/dist/manifest/pyproject/types.d.ts +19 -0
- package/dist/semantic/import-graph.d.ts +69 -0
- package/dist/wasm/interfaces/wasi-cli-environment.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-exit.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-stderr.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-stdin.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-stdout.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-terminal-input.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-terminal-output.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-terminal-stderr.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-terminal-stdin.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-cli-terminal-stdout.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-clocks-monotonic-clock.d.ts +4 -0
- package/dist/wasm/interfaces/wasi-clocks-wall-clock.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-filesystem-preopens.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-filesystem-types.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-io-error.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-io-poll.d.ts +2 -1
- package/dist/wasm/interfaces/wasi-io-streams.d.ts +1 -1
- package/dist/wasm/interfaces/wasi-random-insecure-seed.d.ts +1 -1
- package/dist/wasm/vercel_python_analysis.core.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.core2.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.core3.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.d.ts +44 -34
- package/dist/wasm/vercel_python_analysis.js +292 -140
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,9 @@ __export(src_exports, {
|
|
|
48
48
|
PyProjectProjectSchema: () => PyProjectProjectSchema,
|
|
49
49
|
PyProjectTomlSchema: () => PyProjectTomlSchema,
|
|
50
50
|
PyProjectToolSectionSchema: () => PyProjectToolSectionSchema,
|
|
51
|
+
PyProjectToolVercelFastapiSectionSchema: () => PyProjectToolVercelFastapiSectionSchema,
|
|
52
|
+
PyProjectToolVercelFastapiStaticSectionSchema: () => PyProjectToolVercelFastapiStaticSectionSchema,
|
|
53
|
+
PyProjectToolVercelSectionSchema: () => PyProjectToolVercelSectionSchema,
|
|
51
54
|
PythonAnalysisError: () => PythonAnalysisError,
|
|
52
55
|
PythonBuild: () => PythonBuild,
|
|
53
56
|
PythonConfigKind: () => PythonConfigKind,
|
|
@@ -63,11 +66,13 @@ __export(src_exports, {
|
|
|
63
66
|
UvConfigWorkspaceSchema: () => UvConfigWorkspaceSchema,
|
|
64
67
|
UvIndexEntrySchema: () => UvIndexEntrySchema,
|
|
65
68
|
classifyPackages: () => classifyPackages,
|
|
69
|
+
collectImportClosure: () => collectImportClosure,
|
|
66
70
|
containsTopLevelCallable: () => containsTopLevelCallable,
|
|
67
71
|
createMinimalManifest: () => createMinimalManifest,
|
|
68
72
|
discoverPythonPackage: () => discoverPythonPackage,
|
|
69
73
|
evaluateMarker: () => evaluateMarker,
|
|
70
74
|
extendDistRecord: () => extendDistRecord,
|
|
75
|
+
extractImports: () => extractImports,
|
|
71
76
|
findAppOrHandler: () => findAppOrHandler,
|
|
72
77
|
getStringConstant: () => getStringConstant,
|
|
73
78
|
isPrivatePackageSource: () => isPrivatePackageSource,
|
|
@@ -223,6 +228,224 @@ async function getStringConstant(source, name) {
|
|
|
223
228
|
return mod.getStringConstant(source, name) ?? null;
|
|
224
229
|
}
|
|
225
230
|
|
|
231
|
+
// src/semantic/import-graph.ts
|
|
232
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
233
|
+
var import_path = require("path");
|
|
234
|
+
async function extractImports(source) {
|
|
235
|
+
if (!source.includes("import")) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
const mod = await importWasmModule();
|
|
239
|
+
return mod.extractImports(source);
|
|
240
|
+
}
|
|
241
|
+
var DEFAULT_MAX_FILES = 3e4;
|
|
242
|
+
async function readStatKind(path4) {
|
|
243
|
+
try {
|
|
244
|
+
const stats = await import_fs.default.promises.stat(path4);
|
|
245
|
+
if (stats.isFile())
|
|
246
|
+
return "file";
|
|
247
|
+
if (stats.isDirectory())
|
|
248
|
+
return "dir";
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function statKind(path4, cache) {
|
|
254
|
+
const cached = cache.statKinds.get(path4);
|
|
255
|
+
if (cached)
|
|
256
|
+
return cached;
|
|
257
|
+
const pending = readStatKind(path4);
|
|
258
|
+
cache.statKinds.set(path4, pending);
|
|
259
|
+
return pending;
|
|
260
|
+
}
|
|
261
|
+
async function resolveNamePartsUncached(parts, searchDirs, cache) {
|
|
262
|
+
const rel = (0, import_path.join)(...parts);
|
|
263
|
+
for (const root of searchDirs) {
|
|
264
|
+
let target = null;
|
|
265
|
+
let targetKind = null;
|
|
266
|
+
const candidateInit = (0, import_path.join)(root, rel, "__init__.py");
|
|
267
|
+
if (await statKind(candidateInit, cache) === "file") {
|
|
268
|
+
target = candidateInit;
|
|
269
|
+
targetKind = "file";
|
|
270
|
+
} else {
|
|
271
|
+
const candidateModule = (0, import_path.join)(root, `${rel}.py`);
|
|
272
|
+
if (await statKind(candidateModule, cache) === "file") {
|
|
273
|
+
target = candidateModule;
|
|
274
|
+
targetKind = "file";
|
|
275
|
+
} else if (await statKind((0, import_path.join)(root, rel), cache) === "dir") {
|
|
276
|
+
target = (0, import_path.join)(root, rel);
|
|
277
|
+
targetKind = "dir";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (target !== null) {
|
|
281
|
+
const parentChain = [];
|
|
282
|
+
let acc = root;
|
|
283
|
+
for (const part of parts.slice(0, -1)) {
|
|
284
|
+
acc = (0, import_path.join)(acc, part);
|
|
285
|
+
const init = (0, import_path.join)(acc, "__init__.py");
|
|
286
|
+
if (await statKind(init, cache) === "file") {
|
|
287
|
+
parentChain.push(init);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { target, targetKind, parentChain };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
function resolveNameParts(parts, searchDirs, searchKey, cache) {
|
|
296
|
+
if (parts.length === 0)
|
|
297
|
+
return Promise.resolve(null);
|
|
298
|
+
const key = `${searchKey}\0${parts.join(".")}`;
|
|
299
|
+
const cached = cache.names.get(key);
|
|
300
|
+
if (cached)
|
|
301
|
+
return cached;
|
|
302
|
+
const pending = resolveNamePartsUncached(parts, searchDirs, cache);
|
|
303
|
+
cache.names.set(key, pending);
|
|
304
|
+
return pending;
|
|
305
|
+
}
|
|
306
|
+
async function resolveSeedName(parts, searchDirs, searchKey, cache) {
|
|
307
|
+
for (let length = parts.length; length > 0; length--) {
|
|
308
|
+
const resolved = await resolveNameParts(
|
|
309
|
+
parts.slice(0, length),
|
|
310
|
+
searchDirs,
|
|
311
|
+
searchKey,
|
|
312
|
+
cache
|
|
313
|
+
);
|
|
314
|
+
if (resolved)
|
|
315
|
+
return resolved;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
function submoduleDir(target, targetKind) {
|
|
320
|
+
if (targetKind === "dir")
|
|
321
|
+
return target;
|
|
322
|
+
if (targetKind === "file" && target.endsWith(`${import_path.sep}__init__.py`)) {
|
|
323
|
+
return (0, import_path.dirname)(target);
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
async function resolveImport(stmt, importerPath, roots, rootsKey, cache) {
|
|
328
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
329
|
+
let search = roots;
|
|
330
|
+
let searchKey = rootsKey;
|
|
331
|
+
if (stmt.level > 0) {
|
|
332
|
+
let base = (0, import_path.dirname)(importerPath);
|
|
333
|
+
for (let i = 1; i < stmt.level; i++) {
|
|
334
|
+
base = (0, import_path.dirname)(base);
|
|
335
|
+
}
|
|
336
|
+
search = [base];
|
|
337
|
+
searchKey = base;
|
|
338
|
+
}
|
|
339
|
+
const parts = stmt.module ? stmt.module.split(".") : [];
|
|
340
|
+
let target;
|
|
341
|
+
let targetKind;
|
|
342
|
+
if (parts.length > 0) {
|
|
343
|
+
const name = await resolveNameParts(parts, search, searchKey, cache);
|
|
344
|
+
if (!name)
|
|
345
|
+
return [];
|
|
346
|
+
target = name.target;
|
|
347
|
+
targetKind = name.targetKind;
|
|
348
|
+
if (targetKind === "file")
|
|
349
|
+
resolved.add(target);
|
|
350
|
+
for (const init of name.parentChain) {
|
|
351
|
+
resolved.add(init);
|
|
352
|
+
}
|
|
353
|
+
} else if (stmt.level > 0) {
|
|
354
|
+
target = search[0];
|
|
355
|
+
targetKind = "dir";
|
|
356
|
+
} else {
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
const subDir = submoduleDir(target, targetKind);
|
|
360
|
+
if (subDir) {
|
|
361
|
+
for (const name of stmt.names) {
|
|
362
|
+
if (name === "*")
|
|
363
|
+
continue;
|
|
364
|
+
const sub = await resolveNameParts([name], [subDir], subDir, cache);
|
|
365
|
+
if (sub) {
|
|
366
|
+
if (sub.targetKind === "file")
|
|
367
|
+
resolved.add(sub.target);
|
|
368
|
+
for (const init of sub.parentChain) {
|
|
369
|
+
resolved.add(init);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return [...resolved];
|
|
375
|
+
}
|
|
376
|
+
function isModuleNameSeed(seed) {
|
|
377
|
+
return !(0, import_path.isAbsolute)(seed) && /^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)*$/.test(seed);
|
|
378
|
+
}
|
|
379
|
+
async function collectImportClosure({
|
|
380
|
+
seeds,
|
|
381
|
+
searchRoots,
|
|
382
|
+
maxFiles = DEFAULT_MAX_FILES
|
|
383
|
+
}) {
|
|
384
|
+
const roots = searchRoots.map((r) => (0, import_path.resolve)(r));
|
|
385
|
+
const rootsKey = roots.join("\0");
|
|
386
|
+
const cache = {
|
|
387
|
+
statKinds: /* @__PURE__ */ new Map(),
|
|
388
|
+
names: /* @__PURE__ */ new Map()
|
|
389
|
+
};
|
|
390
|
+
const visited = /* @__PURE__ */ new Set();
|
|
391
|
+
let frontier = [];
|
|
392
|
+
let truncated = false;
|
|
393
|
+
for (const seed of seeds) {
|
|
394
|
+
if (isModuleNameSeed(seed)) {
|
|
395
|
+
const name = await resolveSeedName(
|
|
396
|
+
seed.split("."),
|
|
397
|
+
roots,
|
|
398
|
+
rootsKey,
|
|
399
|
+
cache
|
|
400
|
+
);
|
|
401
|
+
if (name) {
|
|
402
|
+
if (name.targetKind === "file")
|
|
403
|
+
frontier.push(name.target);
|
|
404
|
+
frontier.push(...name.parentChain);
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
const seedPath = (0, import_path.resolve)(seed);
|
|
408
|
+
if (await statKind(seedPath, cache) === "file") {
|
|
409
|
+
frontier.push(seedPath);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
while (frontier.length > 0) {
|
|
414
|
+
const batch = [...new Set(frontier)].filter((p) => !visited.has(p));
|
|
415
|
+
frontier = [];
|
|
416
|
+
if (batch.length === 0)
|
|
417
|
+
continue;
|
|
418
|
+
for (const path4 of batch) {
|
|
419
|
+
visited.add(path4);
|
|
420
|
+
}
|
|
421
|
+
if (visited.size > maxFiles) {
|
|
422
|
+
truncated = true;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
const dependencies = await Promise.all(
|
|
426
|
+
batch.map(async (importerPath) => {
|
|
427
|
+
let source;
|
|
428
|
+
try {
|
|
429
|
+
source = await import_fs.default.promises.readFile(importerPath, "utf8");
|
|
430
|
+
} catch {
|
|
431
|
+
return [];
|
|
432
|
+
}
|
|
433
|
+
const stmts = await extractImports(source);
|
|
434
|
+
const files = await Promise.all(
|
|
435
|
+
stmts.filter((s) => s.isModuleLevel && !s.inTypeChecking).map((s) => resolveImport(s, importerPath, roots, rootsKey, cache))
|
|
436
|
+
);
|
|
437
|
+
return files.flat();
|
|
438
|
+
})
|
|
439
|
+
);
|
|
440
|
+
for (const dep of dependencies.flat()) {
|
|
441
|
+
if (!visited.has(dep)) {
|
|
442
|
+
frontier.push(dep);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return { files: visited, truncated };
|
|
447
|
+
}
|
|
448
|
+
|
|
226
449
|
// src/manifest/dist-metadata.ts
|
|
227
450
|
var import_node_crypto = require("crypto");
|
|
228
451
|
var import_node_fs = require("fs");
|
|
@@ -391,7 +614,7 @@ async function scanDistributions(sitePackagesDir) {
|
|
|
391
614
|
return index;
|
|
392
615
|
}
|
|
393
616
|
function hashFile(filePath) {
|
|
394
|
-
return new Promise((
|
|
617
|
+
return new Promise((resolve2, reject) => {
|
|
395
618
|
const h = (0, import_node_crypto.createHash)("sha256");
|
|
396
619
|
let size = 0;
|
|
397
620
|
const stream = (0, import_node_fs.createReadStream)(filePath);
|
|
@@ -401,7 +624,7 @@ function hashFile(filePath) {
|
|
|
401
624
|
});
|
|
402
625
|
stream.on("error", reject);
|
|
403
626
|
stream.on("end", () => {
|
|
404
|
-
|
|
627
|
+
resolve2({ hash: h.digest("base64url"), size });
|
|
405
628
|
});
|
|
406
629
|
});
|
|
407
630
|
}
|
|
@@ -1008,8 +1231,18 @@ var dependencyGroupEntrySchema = import_zod4.z.union([
|
|
|
1008
1231
|
var pyProjectDependencyGroupsSchema = import_zod4.z.record(
|
|
1009
1232
|
import_zod4.z.array(dependencyGroupEntrySchema)
|
|
1010
1233
|
);
|
|
1234
|
+
var pyProjectToolVercelFastapiStaticSectionSchema = import_zod4.z.object({
|
|
1235
|
+
cdn: import_zod4.z.boolean().optional()
|
|
1236
|
+
});
|
|
1237
|
+
var pyProjectToolVercelFastapiSectionSchema = import_zod4.z.object({
|
|
1238
|
+
static: pyProjectToolVercelFastapiStaticSectionSchema.optional()
|
|
1239
|
+
});
|
|
1240
|
+
var pyProjectToolVercelSectionSchema = import_zod4.z.object({
|
|
1241
|
+
fastapi: pyProjectToolVercelFastapiSectionSchema.optional()
|
|
1242
|
+
});
|
|
1011
1243
|
var pyProjectToolSectionSchema = import_zod4.z.object({
|
|
1012
|
-
uv: uvConfigSchema.optional()
|
|
1244
|
+
uv: uvConfigSchema.optional(),
|
|
1245
|
+
vercel: pyProjectToolVercelSectionSchema.optional()
|
|
1013
1246
|
});
|
|
1014
1247
|
var pyProjectTomlSchema = import_zod4.z.object({
|
|
1015
1248
|
project: pyProjectProjectSchema.optional(),
|
|
@@ -1027,8 +1260,11 @@ var LicenseObjectSchema = licenseObjectSchema.passthrough();
|
|
|
1027
1260
|
var LicenseSchema = licenseSchema;
|
|
1028
1261
|
var PyProjectProjectSchema = pyProjectProjectSchema.passthrough();
|
|
1029
1262
|
var PyProjectDependencyGroupsSchema = pyProjectDependencyGroupsSchema;
|
|
1263
|
+
var PyProjectToolVercelSectionSchema = pyProjectToolVercelSectionSchema;
|
|
1264
|
+
var PyProjectToolVercelFastapiSectionSchema = pyProjectToolVercelFastapiSectionSchema;
|
|
1265
|
+
var PyProjectToolVercelFastapiStaticSectionSchema = pyProjectToolVercelFastapiStaticSectionSchema;
|
|
1030
1266
|
var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
|
|
1031
|
-
var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
|
|
1267
|
+
var PyProjectTomlSchema = pyProjectTomlSchema.extend({ tool: PyProjectToolSectionSchema.optional() }).passthrough();
|
|
1032
1268
|
|
|
1033
1269
|
// src/manifest/requirements-txt-parser.ts
|
|
1034
1270
|
var import_posix2 = require("path/posix");
|
|
@@ -2401,6 +2637,9 @@ var HashDigestSchema = hashDigestSchema;
|
|
|
2401
2637
|
PyProjectProjectSchema,
|
|
2402
2638
|
PyProjectTomlSchema,
|
|
2403
2639
|
PyProjectToolSectionSchema,
|
|
2640
|
+
PyProjectToolVercelFastapiSectionSchema,
|
|
2641
|
+
PyProjectToolVercelFastapiStaticSectionSchema,
|
|
2642
|
+
PyProjectToolVercelSectionSchema,
|
|
2404
2643
|
PythonAnalysisError,
|
|
2405
2644
|
PythonBuild,
|
|
2406
2645
|
PythonConfigKind,
|
|
@@ -2416,11 +2655,13 @@ var HashDigestSchema = hashDigestSchema;
|
|
|
2416
2655
|
UvConfigWorkspaceSchema,
|
|
2417
2656
|
UvIndexEntrySchema,
|
|
2418
2657
|
classifyPackages,
|
|
2658
|
+
collectImportClosure,
|
|
2419
2659
|
containsTopLevelCallable,
|
|
2420
2660
|
createMinimalManifest,
|
|
2421
2661
|
discoverPythonPackage,
|
|
2422
2662
|
evaluateMarker,
|
|
2423
2663
|
extendDistRecord,
|
|
2664
|
+
extractImports,
|
|
2424
2665
|
findAppOrHandler,
|
|
2425
2666
|
getStringConstant,
|
|
2426
2667
|
isPrivatePackageSource,
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @module @vercel/python-analysis
|
|
8
8
|
*/
|
|
9
9
|
export { findAppOrHandler, containsTopLevelCallable, getStringConstant, } from './semantic/entrypoints';
|
|
10
|
+
export { collectImportClosure, extractImports } from './semantic/import-graph';
|
|
11
|
+
export type { ImportClosureOptions, ImportClosureResult, ImportStmt, } from './semantic/import-graph';
|
|
10
12
|
export type { Distribution, DistributionIndex, PackagePath, DirectUrlInfo, } from './manifest/dist-metadata';
|
|
11
13
|
export { extendDistRecord, scanDistributions } from './manifest/dist-metadata';
|
|
12
14
|
export type { PythonConfig, PythonConfigs, PythonLockFile, PythonManifest, PythonManifestOrigin, PythonPackage, PythonVersionConfig, } from './manifest/package';
|
|
@@ -14,12 +16,12 @@ export { discoverPythonPackage, PythonConfigKind, PythonLockFileKind, PythonMani
|
|
|
14
16
|
export { createMinimalManifest, stringifyManifest, type CreateMinimalManifestOptions, } from './manifest/serialize';
|
|
15
17
|
export type { ClassifyPackagesOptions, PackageClassification, UvLockFile, UvLockPackage, UvLockPackageSource, UvLockWheel, } from './manifest/uv-lock-parser';
|
|
16
18
|
export { classifyPackages, isPrivatePackageSource, normalizePackageName, parseUvLock, } from './manifest/uv-lock-parser';
|
|
17
|
-
export { evaluateMarker, isWheelCompatible
|
|
19
|
+
export { evaluateMarker, isWheelCompatible } from './manifest/wheel-compat';
|
|
18
20
|
export type { PythonSelectionResult, PythonVersionSelectionResult, } from './manifest/python-selector';
|
|
19
21
|
export { selectPython, selectPythonVersion } from './manifest/python-selector';
|
|
20
22
|
export { PythonAnalysisError } from './util/error';
|
|
21
|
-
export { LicenseObjectSchema, LicenseSchema, PersonSchema, PyProjectBuildSystemSchema, PyProjectDependencyGroupsSchema, PyProjectProjectSchema, PyProjectToolSectionSchema, PyProjectTomlSchema, ReadmeObjectSchema, ReadmeSchema, } from './manifest/pyproject/schema';
|
|
22
|
-
export type { DependencyGroupEntry, DependencyGroupInclude, License, LicenseObject, Person, PyProjectBuildSystem, PyProjectDependencyGroups, PyProjectProject, PyProjectToml, PyProjectToolSection, Readme, ReadmeObject, } from './manifest/pyproject/types';
|
|
23
|
+
export { LicenseObjectSchema, LicenseSchema, PersonSchema, PyProjectBuildSystemSchema, PyProjectDependencyGroupsSchema, PyProjectProjectSchema, PyProjectToolSectionSchema, PyProjectTomlSchema, ReadmeObjectSchema, ReadmeSchema, PyProjectToolVercelFastapiSectionSchema, PyProjectToolVercelFastapiStaticSectionSchema, PyProjectToolVercelSectionSchema, } from './manifest/pyproject/schema';
|
|
24
|
+
export type { DependencyGroupEntry, DependencyGroupInclude, License, LicenseObject, Person, PyProjectBuildSystem, PyProjectDependencyGroups, PyProjectProject, PyProjectToml, PyProjectToolSection, Readme, ReadmeObject, PyProjectToolVercelFastapiSection, PyProjectToolVercelFastapiStaticSection, PyProjectToolVercelSection, } from './manifest/pyproject/types';
|
|
23
25
|
export { UvConfigSchema, UvConfigWorkspaceSchema, UvIndexEntrySchema, } from './manifest/uv-config/schema';
|
|
24
26
|
export type { UvConfig, UvConfigWorkspace, UvIndexEntry, } from './manifest/uv-config/types';
|
|
25
27
|
export { PipfileDependencyDetailSchema, PipfileDependencySchema, PipfileLikeSchema, PipfileLockLikeSchema, PipfileLockMetaSchema, PipfileSourceSchema, } from './manifest/pipfile/schema';
|
package/dist/index.js
CHANGED
|
@@ -139,11 +139,229 @@ async function getStringConstant(source, name) {
|
|
|
139
139
|
return mod.getStringConstant(source, name) ?? null;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// src/semantic/import-graph.ts
|
|
143
|
+
import fs from "fs";
|
|
144
|
+
import { isAbsolute, join as join2, dirname as dirname2, resolve, sep } from "path";
|
|
145
|
+
async function extractImports(source) {
|
|
146
|
+
if (!source.includes("import")) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
const mod = await importWasmModule();
|
|
150
|
+
return mod.extractImports(source);
|
|
151
|
+
}
|
|
152
|
+
var DEFAULT_MAX_FILES = 3e4;
|
|
153
|
+
async function readStatKind(path4) {
|
|
154
|
+
try {
|
|
155
|
+
const stats = await fs.promises.stat(path4);
|
|
156
|
+
if (stats.isFile())
|
|
157
|
+
return "file";
|
|
158
|
+
if (stats.isDirectory())
|
|
159
|
+
return "dir";
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function statKind(path4, cache) {
|
|
165
|
+
const cached = cache.statKinds.get(path4);
|
|
166
|
+
if (cached)
|
|
167
|
+
return cached;
|
|
168
|
+
const pending = readStatKind(path4);
|
|
169
|
+
cache.statKinds.set(path4, pending);
|
|
170
|
+
return pending;
|
|
171
|
+
}
|
|
172
|
+
async function resolveNamePartsUncached(parts, searchDirs, cache) {
|
|
173
|
+
const rel = join2(...parts);
|
|
174
|
+
for (const root of searchDirs) {
|
|
175
|
+
let target = null;
|
|
176
|
+
let targetKind = null;
|
|
177
|
+
const candidateInit = join2(root, rel, "__init__.py");
|
|
178
|
+
if (await statKind(candidateInit, cache) === "file") {
|
|
179
|
+
target = candidateInit;
|
|
180
|
+
targetKind = "file";
|
|
181
|
+
} else {
|
|
182
|
+
const candidateModule = join2(root, `${rel}.py`);
|
|
183
|
+
if (await statKind(candidateModule, cache) === "file") {
|
|
184
|
+
target = candidateModule;
|
|
185
|
+
targetKind = "file";
|
|
186
|
+
} else if (await statKind(join2(root, rel), cache) === "dir") {
|
|
187
|
+
target = join2(root, rel);
|
|
188
|
+
targetKind = "dir";
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (target !== null) {
|
|
192
|
+
const parentChain = [];
|
|
193
|
+
let acc = root;
|
|
194
|
+
for (const part of parts.slice(0, -1)) {
|
|
195
|
+
acc = join2(acc, part);
|
|
196
|
+
const init = join2(acc, "__init__.py");
|
|
197
|
+
if (await statKind(init, cache) === "file") {
|
|
198
|
+
parentChain.push(init);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { target, targetKind, parentChain };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function resolveNameParts(parts, searchDirs, searchKey, cache) {
|
|
207
|
+
if (parts.length === 0)
|
|
208
|
+
return Promise.resolve(null);
|
|
209
|
+
const key = `${searchKey}\0${parts.join(".")}`;
|
|
210
|
+
const cached = cache.names.get(key);
|
|
211
|
+
if (cached)
|
|
212
|
+
return cached;
|
|
213
|
+
const pending = resolveNamePartsUncached(parts, searchDirs, cache);
|
|
214
|
+
cache.names.set(key, pending);
|
|
215
|
+
return pending;
|
|
216
|
+
}
|
|
217
|
+
async function resolveSeedName(parts, searchDirs, searchKey, cache) {
|
|
218
|
+
for (let length = parts.length; length > 0; length--) {
|
|
219
|
+
const resolved = await resolveNameParts(
|
|
220
|
+
parts.slice(0, length),
|
|
221
|
+
searchDirs,
|
|
222
|
+
searchKey,
|
|
223
|
+
cache
|
|
224
|
+
);
|
|
225
|
+
if (resolved)
|
|
226
|
+
return resolved;
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
function submoduleDir(target, targetKind) {
|
|
231
|
+
if (targetKind === "dir")
|
|
232
|
+
return target;
|
|
233
|
+
if (targetKind === "file" && target.endsWith(`${sep}__init__.py`)) {
|
|
234
|
+
return dirname2(target);
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
async function resolveImport(stmt, importerPath, roots, rootsKey, cache) {
|
|
239
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
240
|
+
let search = roots;
|
|
241
|
+
let searchKey = rootsKey;
|
|
242
|
+
if (stmt.level > 0) {
|
|
243
|
+
let base = dirname2(importerPath);
|
|
244
|
+
for (let i = 1; i < stmt.level; i++) {
|
|
245
|
+
base = dirname2(base);
|
|
246
|
+
}
|
|
247
|
+
search = [base];
|
|
248
|
+
searchKey = base;
|
|
249
|
+
}
|
|
250
|
+
const parts = stmt.module ? stmt.module.split(".") : [];
|
|
251
|
+
let target;
|
|
252
|
+
let targetKind;
|
|
253
|
+
if (parts.length > 0) {
|
|
254
|
+
const name = await resolveNameParts(parts, search, searchKey, cache);
|
|
255
|
+
if (!name)
|
|
256
|
+
return [];
|
|
257
|
+
target = name.target;
|
|
258
|
+
targetKind = name.targetKind;
|
|
259
|
+
if (targetKind === "file")
|
|
260
|
+
resolved.add(target);
|
|
261
|
+
for (const init of name.parentChain) {
|
|
262
|
+
resolved.add(init);
|
|
263
|
+
}
|
|
264
|
+
} else if (stmt.level > 0) {
|
|
265
|
+
target = search[0];
|
|
266
|
+
targetKind = "dir";
|
|
267
|
+
} else {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
const subDir = submoduleDir(target, targetKind);
|
|
271
|
+
if (subDir) {
|
|
272
|
+
for (const name of stmt.names) {
|
|
273
|
+
if (name === "*")
|
|
274
|
+
continue;
|
|
275
|
+
const sub = await resolveNameParts([name], [subDir], subDir, cache);
|
|
276
|
+
if (sub) {
|
|
277
|
+
if (sub.targetKind === "file")
|
|
278
|
+
resolved.add(sub.target);
|
|
279
|
+
for (const init of sub.parentChain) {
|
|
280
|
+
resolved.add(init);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return [...resolved];
|
|
286
|
+
}
|
|
287
|
+
function isModuleNameSeed(seed) {
|
|
288
|
+
return !isAbsolute(seed) && /^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)*$/.test(seed);
|
|
289
|
+
}
|
|
290
|
+
async function collectImportClosure({
|
|
291
|
+
seeds,
|
|
292
|
+
searchRoots,
|
|
293
|
+
maxFiles = DEFAULT_MAX_FILES
|
|
294
|
+
}) {
|
|
295
|
+
const roots = searchRoots.map((r) => resolve(r));
|
|
296
|
+
const rootsKey = roots.join("\0");
|
|
297
|
+
const cache = {
|
|
298
|
+
statKinds: /* @__PURE__ */ new Map(),
|
|
299
|
+
names: /* @__PURE__ */ new Map()
|
|
300
|
+
};
|
|
301
|
+
const visited = /* @__PURE__ */ new Set();
|
|
302
|
+
let frontier = [];
|
|
303
|
+
let truncated = false;
|
|
304
|
+
for (const seed of seeds) {
|
|
305
|
+
if (isModuleNameSeed(seed)) {
|
|
306
|
+
const name = await resolveSeedName(
|
|
307
|
+
seed.split("."),
|
|
308
|
+
roots,
|
|
309
|
+
rootsKey,
|
|
310
|
+
cache
|
|
311
|
+
);
|
|
312
|
+
if (name) {
|
|
313
|
+
if (name.targetKind === "file")
|
|
314
|
+
frontier.push(name.target);
|
|
315
|
+
frontier.push(...name.parentChain);
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
const seedPath = resolve(seed);
|
|
319
|
+
if (await statKind(seedPath, cache) === "file") {
|
|
320
|
+
frontier.push(seedPath);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
while (frontier.length > 0) {
|
|
325
|
+
const batch = [...new Set(frontier)].filter((p) => !visited.has(p));
|
|
326
|
+
frontier = [];
|
|
327
|
+
if (batch.length === 0)
|
|
328
|
+
continue;
|
|
329
|
+
for (const path4 of batch) {
|
|
330
|
+
visited.add(path4);
|
|
331
|
+
}
|
|
332
|
+
if (visited.size > maxFiles) {
|
|
333
|
+
truncated = true;
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
const dependencies = await Promise.all(
|
|
337
|
+
batch.map(async (importerPath) => {
|
|
338
|
+
let source;
|
|
339
|
+
try {
|
|
340
|
+
source = await fs.promises.readFile(importerPath, "utf8");
|
|
341
|
+
} catch {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
const stmts = await extractImports(source);
|
|
345
|
+
const files = await Promise.all(
|
|
346
|
+
stmts.filter((s) => s.isModuleLevel && !s.inTypeChecking).map((s) => resolveImport(s, importerPath, roots, rootsKey, cache))
|
|
347
|
+
);
|
|
348
|
+
return files.flat();
|
|
349
|
+
})
|
|
350
|
+
);
|
|
351
|
+
for (const dep of dependencies.flat()) {
|
|
352
|
+
if (!visited.has(dep)) {
|
|
353
|
+
frontier.push(dep);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return { files: visited, truncated };
|
|
358
|
+
}
|
|
359
|
+
|
|
142
360
|
// src/manifest/dist-metadata.ts
|
|
143
361
|
import { createHash } from "crypto";
|
|
144
362
|
import { createReadStream } from "fs";
|
|
145
363
|
import { appendFile, readdir, readFile as readFile2 } from "fs/promises";
|
|
146
|
-
import { join as
|
|
364
|
+
import { join as join3 } from "path";
|
|
147
365
|
|
|
148
366
|
// src/manifest/pep508.ts
|
|
149
367
|
var EXTRAS_REGEX = /^(.+)\[([^\]]+)\]$/;
|
|
@@ -222,7 +440,7 @@ async function parsePep508(depOrDeps) {
|
|
|
222
440
|
// src/manifest/dist-metadata.ts
|
|
223
441
|
async function readDistInfoFile(distInfoDir, filename) {
|
|
224
442
|
try {
|
|
225
|
-
return await readFile2(
|
|
443
|
+
return await readFile2(join3(distInfoDir, filename), "utf-8");
|
|
226
444
|
} catch {
|
|
227
445
|
return void 0;
|
|
228
446
|
}
|
|
@@ -238,7 +456,7 @@ async function scanDistributions(sitePackagesDir) {
|
|
|
238
456
|
}
|
|
239
457
|
const distInfoDirs = entries.filter((e) => e.endsWith(".dist-info"));
|
|
240
458
|
for (const dirName of distInfoDirs) {
|
|
241
|
-
const distInfoPath =
|
|
459
|
+
const distInfoPath = join3(sitePackagesDir, dirName);
|
|
242
460
|
const metadataContent = await readDistInfoFile(distInfoPath, "METADATA");
|
|
243
461
|
if (!metadataContent) {
|
|
244
462
|
console.debug(`Missing METADATA in ${dirName}`);
|
|
@@ -307,7 +525,7 @@ async function scanDistributions(sitePackagesDir) {
|
|
|
307
525
|
return index;
|
|
308
526
|
}
|
|
309
527
|
function hashFile(filePath) {
|
|
310
|
-
return new Promise((
|
|
528
|
+
return new Promise((resolve2, reject) => {
|
|
311
529
|
const h = createHash("sha256");
|
|
312
530
|
let size = 0;
|
|
313
531
|
const stream = createReadStream(filePath);
|
|
@@ -317,7 +535,7 @@ function hashFile(filePath) {
|
|
|
317
535
|
});
|
|
318
536
|
stream.on("error", reject);
|
|
319
537
|
stream.on("end", () => {
|
|
320
|
-
|
|
538
|
+
resolve2({ hash: h.digest("base64url"), size });
|
|
321
539
|
});
|
|
322
540
|
});
|
|
323
541
|
}
|
|
@@ -339,7 +557,7 @@ async function extendDistRecord(sitePackagesDir, packageName, paths) {
|
|
|
339
557
|
`No .dist-info directory found for package "${packageName}" in ${sitePackagesDir}`
|
|
340
558
|
);
|
|
341
559
|
}
|
|
342
|
-
const recordPath =
|
|
560
|
+
const recordPath = join3(sitePackagesDir, distInfoDirName, "RECORD");
|
|
343
561
|
let existingRecord;
|
|
344
562
|
try {
|
|
345
563
|
existingRecord = await readFile2(recordPath, "utf-8");
|
|
@@ -354,7 +572,7 @@ async function extendDistRecord(sitePackagesDir, packageName, paths) {
|
|
|
354
572
|
const prefix = existingRecord.length > 0 && !existingRecord.endsWith("\n") ? "\n" : "";
|
|
355
573
|
const lines = [];
|
|
356
574
|
for (const p of newEntries) {
|
|
357
|
-
const fullPath =
|
|
575
|
+
const fullPath = join3(sitePackagesDir, p);
|
|
358
576
|
const { hash, size } = await hashFile(fullPath);
|
|
359
577
|
lines.push(`${p},sha256=${hash},${size}`);
|
|
360
578
|
}
|
|
@@ -927,8 +1145,18 @@ var dependencyGroupEntrySchema = z4.union([
|
|
|
927
1145
|
var pyProjectDependencyGroupsSchema = z4.record(
|
|
928
1146
|
z4.array(dependencyGroupEntrySchema)
|
|
929
1147
|
);
|
|
1148
|
+
var pyProjectToolVercelFastapiStaticSectionSchema = z4.object({
|
|
1149
|
+
cdn: z4.boolean().optional()
|
|
1150
|
+
});
|
|
1151
|
+
var pyProjectToolVercelFastapiSectionSchema = z4.object({
|
|
1152
|
+
static: pyProjectToolVercelFastapiStaticSectionSchema.optional()
|
|
1153
|
+
});
|
|
1154
|
+
var pyProjectToolVercelSectionSchema = z4.object({
|
|
1155
|
+
fastapi: pyProjectToolVercelFastapiSectionSchema.optional()
|
|
1156
|
+
});
|
|
930
1157
|
var pyProjectToolSectionSchema = z4.object({
|
|
931
|
-
uv: uvConfigSchema.optional()
|
|
1158
|
+
uv: uvConfigSchema.optional(),
|
|
1159
|
+
vercel: pyProjectToolVercelSectionSchema.optional()
|
|
932
1160
|
});
|
|
933
1161
|
var pyProjectTomlSchema = z4.object({
|
|
934
1162
|
project: pyProjectProjectSchema.optional(),
|
|
@@ -946,11 +1174,14 @@ var LicenseObjectSchema = licenseObjectSchema.passthrough();
|
|
|
946
1174
|
var LicenseSchema = licenseSchema;
|
|
947
1175
|
var PyProjectProjectSchema = pyProjectProjectSchema.passthrough();
|
|
948
1176
|
var PyProjectDependencyGroupsSchema = pyProjectDependencyGroupsSchema;
|
|
1177
|
+
var PyProjectToolVercelSectionSchema = pyProjectToolVercelSectionSchema;
|
|
1178
|
+
var PyProjectToolVercelFastapiSectionSchema = pyProjectToolVercelFastapiSectionSchema;
|
|
1179
|
+
var PyProjectToolVercelFastapiStaticSectionSchema = pyProjectToolVercelFastapiStaticSectionSchema;
|
|
949
1180
|
var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
|
|
950
|
-
var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
|
|
1181
|
+
var PyProjectTomlSchema = pyProjectTomlSchema.extend({ tool: PyProjectToolSectionSchema.optional() }).passthrough();
|
|
951
1182
|
|
|
952
1183
|
// src/manifest/requirements-txt-parser.ts
|
|
953
|
-
import { isAbsolute, join as
|
|
1184
|
+
import { isAbsolute as isAbsolute2, join as join4, relative } from "path/posix";
|
|
954
1185
|
var PRIMARY_INDEX_NAME = "primary";
|
|
955
1186
|
var EXTRA_INDEX_PREFIX = "extra-";
|
|
956
1187
|
var FIND_LINKS_PREFIX = "find-links-";
|
|
@@ -1015,11 +1246,11 @@ function wasmEntryToNormalized(entry, editable) {
|
|
|
1015
1246
|
function rebasePath(p, workingDir, packageRoot) {
|
|
1016
1247
|
if (!workingDir || !packageRoot || workingDir === packageRoot)
|
|
1017
1248
|
return p;
|
|
1018
|
-
if (
|
|
1249
|
+
if (isAbsolute2(p))
|
|
1019
1250
|
return p;
|
|
1020
|
-
const abs =
|
|
1251
|
+
const abs = join4(workingDir, p);
|
|
1021
1252
|
const rel = relative(packageRoot, abs);
|
|
1022
|
-
if (
|
|
1253
|
+
if (isAbsolute2(rel))
|
|
1023
1254
|
return rel;
|
|
1024
1255
|
if (rel.startsWith(".."))
|
|
1025
1256
|
return rel;
|
|
@@ -2319,6 +2550,9 @@ export {
|
|
|
2319
2550
|
PyProjectProjectSchema,
|
|
2320
2551
|
PyProjectTomlSchema,
|
|
2321
2552
|
PyProjectToolSectionSchema,
|
|
2553
|
+
PyProjectToolVercelFastapiSectionSchema,
|
|
2554
|
+
PyProjectToolVercelFastapiStaticSectionSchema,
|
|
2555
|
+
PyProjectToolVercelSectionSchema,
|
|
2322
2556
|
PythonAnalysisError,
|
|
2323
2557
|
PythonBuild,
|
|
2324
2558
|
PythonConfigKind,
|
|
@@ -2334,11 +2568,13 @@ export {
|
|
|
2334
2568
|
UvConfigWorkspaceSchema,
|
|
2335
2569
|
UvIndexEntrySchema,
|
|
2336
2570
|
classifyPackages,
|
|
2571
|
+
collectImportClosure,
|
|
2337
2572
|
containsTopLevelCallable,
|
|
2338
2573
|
createMinimalManifest,
|
|
2339
2574
|
discoverPythonPackage,
|
|
2340
2575
|
evaluateMarker,
|
|
2341
2576
|
extendDistRecord,
|
|
2577
|
+
extractImports,
|
|
2342
2578
|
findAppOrHandler,
|
|
2343
2579
|
getStringConstant,
|
|
2344
2580
|
isPrivatePackageSource,
|