@vercel/python-analysis 0.12.0 → 0.13.2
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 +224 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +231 -11
- package/dist/semantic/import-graph.d.ts +69 -0
- package/dist/wasm/vercel_python_analysis.core.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.d.ts +8 -0
- package/dist/wasm/vercel_python_analysis.js +92 -13
- package/package.json +5 -6
- package/dist/wasm/host-utils.d.ts +0 -22
- package/dist/wasm/load.d.ts +0 -4
package/dist/index.cjs
CHANGED
|
@@ -66,11 +66,13 @@ __export(src_exports, {
|
|
|
66
66
|
UvConfigWorkspaceSchema: () => UvConfigWorkspaceSchema,
|
|
67
67
|
UvIndexEntrySchema: () => UvIndexEntrySchema,
|
|
68
68
|
classifyPackages: () => classifyPackages,
|
|
69
|
+
collectImportClosure: () => collectImportClosure,
|
|
69
70
|
containsTopLevelCallable: () => containsTopLevelCallable,
|
|
70
71
|
createMinimalManifest: () => createMinimalManifest,
|
|
71
72
|
discoverPythonPackage: () => discoverPythonPackage,
|
|
72
73
|
evaluateMarker: () => evaluateMarker,
|
|
73
74
|
extendDistRecord: () => extendDistRecord,
|
|
75
|
+
extractImports: () => extractImports,
|
|
74
76
|
findAppOrHandler: () => findAppOrHandler,
|
|
75
77
|
getStringConstant: () => getStringConstant,
|
|
76
78
|
isPrivatePackageSource: () => isPrivatePackageSource,
|
|
@@ -226,6 +228,224 @@ async function getStringConstant(source, name) {
|
|
|
226
228
|
return mod.getStringConstant(source, name) ?? null;
|
|
227
229
|
}
|
|
228
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
|
+
|
|
229
449
|
// src/manifest/dist-metadata.ts
|
|
230
450
|
var import_node_crypto = require("crypto");
|
|
231
451
|
var import_node_fs = require("fs");
|
|
@@ -394,7 +614,7 @@ async function scanDistributions(sitePackagesDir) {
|
|
|
394
614
|
return index;
|
|
395
615
|
}
|
|
396
616
|
function hashFile(filePath) {
|
|
397
|
-
return new Promise((
|
|
617
|
+
return new Promise((resolve2, reject) => {
|
|
398
618
|
const h = (0, import_node_crypto.createHash)("sha256");
|
|
399
619
|
let size = 0;
|
|
400
620
|
const stream = (0, import_node_fs.createReadStream)(filePath);
|
|
@@ -404,7 +624,7 @@ function hashFile(filePath) {
|
|
|
404
624
|
});
|
|
405
625
|
stream.on("error", reject);
|
|
406
626
|
stream.on("end", () => {
|
|
407
|
-
|
|
627
|
+
resolve2({ hash: h.digest("base64url"), size });
|
|
408
628
|
});
|
|
409
629
|
});
|
|
410
630
|
}
|
|
@@ -2435,11 +2655,13 @@ var HashDigestSchema = hashDigestSchema;
|
|
|
2435
2655
|
UvConfigWorkspaceSchema,
|
|
2436
2656
|
UvIndexEntrySchema,
|
|
2437
2657
|
classifyPackages,
|
|
2658
|
+
collectImportClosure,
|
|
2438
2659
|
containsTopLevelCallable,
|
|
2439
2660
|
createMinimalManifest,
|
|
2440
2661
|
discoverPythonPackage,
|
|
2441
2662
|
evaluateMarker,
|
|
2442
2663
|
extendDistRecord,
|
|
2664
|
+
extractImports,
|
|
2443
2665
|
findAppOrHandler,
|
|
2444
2666
|
getStringConstant,
|
|
2445
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,7 +16,7 @@ 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';
|
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
|
}
|
|
@@ -963,7 +1181,7 @@ var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
|
|
|
963
1181
|
var PyProjectTomlSchema = pyProjectTomlSchema.extend({ tool: PyProjectToolSectionSchema.optional() }).passthrough();
|
|
964
1182
|
|
|
965
1183
|
// src/manifest/requirements-txt-parser.ts
|
|
966
|
-
import { isAbsolute, join as
|
|
1184
|
+
import { isAbsolute as isAbsolute2, join as join4, relative } from "path/posix";
|
|
967
1185
|
var PRIMARY_INDEX_NAME = "primary";
|
|
968
1186
|
var EXTRA_INDEX_PREFIX = "extra-";
|
|
969
1187
|
var FIND_LINKS_PREFIX = "find-links-";
|
|
@@ -1028,11 +1246,11 @@ function wasmEntryToNormalized(entry, editable) {
|
|
|
1028
1246
|
function rebasePath(p, workingDir, packageRoot) {
|
|
1029
1247
|
if (!workingDir || !packageRoot || workingDir === packageRoot)
|
|
1030
1248
|
return p;
|
|
1031
|
-
if (
|
|
1249
|
+
if (isAbsolute2(p))
|
|
1032
1250
|
return p;
|
|
1033
|
-
const abs =
|
|
1251
|
+
const abs = join4(workingDir, p);
|
|
1034
1252
|
const rel = relative(packageRoot, abs);
|
|
1035
|
-
if (
|
|
1253
|
+
if (isAbsolute2(rel))
|
|
1036
1254
|
return rel;
|
|
1037
1255
|
if (rel.startsWith(".."))
|
|
1038
1256
|
return rel;
|
|
@@ -2350,11 +2568,13 @@ export {
|
|
|
2350
2568
|
UvConfigWorkspaceSchema,
|
|
2351
2569
|
UvIndexEntrySchema,
|
|
2352
2570
|
classifyPackages,
|
|
2571
|
+
collectImportClosure,
|
|
2353
2572
|
containsTopLevelCallable,
|
|
2354
2573
|
createMinimalManifest,
|
|
2355
2574
|
discoverPythonPackage,
|
|
2356
2575
|
evaluateMarker,
|
|
2357
2576
|
extendDistRecord,
|
|
2577
|
+
extractImports,
|
|
2358
2578
|
findAppOrHandler,
|
|
2359
2579
|
getStringConstant,
|
|
2360
2580
|
isPrivatePackageSource,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static import-graph analysis: compute the transitive closure of modules a
|
|
3
|
+
* Python application imports at startup, without executing any user code.
|
|
4
|
+
*
|
|
5
|
+
* The closure feeds bytecode packing. Both error directions are safe:
|
|
6
|
+
* over-included modules waste capacity, and missed modules (plugin loaders,
|
|
7
|
+
* imports made by compiled extensions) still ship from residual capacity.
|
|
8
|
+
*
|
|
9
|
+
* Resolution semantics mirror CPython:
|
|
10
|
+
* - dotted name -> `a/b/__init__.py` | `a/b.py` | PEP 420 namespace dir
|
|
11
|
+
* - importing `a.b.c` executes every parent `__init__.py` (emitted + recursed)
|
|
12
|
+
* - `from a import b` probes `b` as a submodule of `a`
|
|
13
|
+
* - relative imports resolve against the importer's package by dot level
|
|
14
|
+
* - first search root wins, so app modules shadow vendor (runtime sys.path)
|
|
15
|
+
*
|
|
16
|
+
* Startup heuristics (applied on top of the raw WASM extraction):
|
|
17
|
+
* - imports inside function bodies are lazy and excluded
|
|
18
|
+
* - `if TYPE_CHECKING:` blocks never run at runtime and are excluded
|
|
19
|
+
* - both branches of module-level `if` / `try` are included
|
|
20
|
+
*/
|
|
21
|
+
export interface ImportStmt {
|
|
22
|
+
/** Dotted module path for from-imports; undefined for `from . import x`. */
|
|
23
|
+
module?: string;
|
|
24
|
+
/** Relative-import level (number of leading dots). 0 = absolute. */
|
|
25
|
+
level: number;
|
|
26
|
+
/** Imported names for from-imports (each may be a submodule). */
|
|
27
|
+
names: string[];
|
|
28
|
+
/** False when nested in a function body (lazy import). */
|
|
29
|
+
isModuleLevel: boolean;
|
|
30
|
+
/** True when under `if TYPE_CHECKING:` (never runs at runtime). */
|
|
31
|
+
inTypeChecking: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extract every import statement in Python source with its syntactic
|
|
35
|
+
* context. Returns an empty list for invalid syntax.
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractImports(source: string): Promise<ImportStmt[]>;
|
|
38
|
+
export interface ImportClosureOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Entry points of the closure: absolute `.py` file paths and/or dotted
|
|
41
|
+
* module or object names (e.g. Django's ROOT_URLCONF / INSTALLED_APPS
|
|
42
|
+
* strings), resolved to their longest importable prefix against searchRoots.
|
|
43
|
+
*/
|
|
44
|
+
seeds: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Ordered module search roots (e.g. [workPath, ...sitePackageDirs]).
|
|
47
|
+
* First match wins, so earlier roots shadow later ones.
|
|
48
|
+
*/
|
|
49
|
+
searchRoots: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Approximate safety bound on parsed files, checked per frontier batch.
|
|
52
|
+
* On overflow the partial closure is returned with `truncated: true`
|
|
53
|
+
* (remaining modules fall to the residual bytecode tier).
|
|
54
|
+
*/
|
|
55
|
+
maxFiles?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface ImportClosureResult {
|
|
58
|
+
/** Absolute paths of every `.py` file imported at startup. */
|
|
59
|
+
files: Set<string>;
|
|
60
|
+
truncated: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Compute the transitive closure of modules imported at startup.
|
|
64
|
+
*
|
|
65
|
+
* Only module-level imports outside `if TYPE_CHECKING:` blocks are followed
|
|
66
|
+
* (see module docstring). Files are read and parsed frontier-by-frontier in
|
|
67
|
+
* parallel; cycles are cut by the visited set.
|
|
68
|
+
*/
|
|
69
|
+
export declare function collectImportClosure({ seeds, searchRoots, maxFiles, }: ImportClosureOptions): Promise<ImportClosureResult>;
|
|
Binary file
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
// world root:component/root
|
|
2
|
+
export interface ImportStmt {
|
|
3
|
+
module?: string,
|
|
4
|
+
level: number,
|
|
5
|
+
names: Array<string>,
|
|
6
|
+
isModuleLevel: boolean,
|
|
7
|
+
inTypeChecking: boolean,
|
|
8
|
+
}
|
|
2
9
|
export interface RecordEntry {
|
|
3
10
|
path: string,
|
|
4
11
|
hash?: string,
|
|
@@ -121,6 +128,7 @@ export interface Root {
|
|
|
121
128
|
findAppOrHandler(source: string): string | undefined,
|
|
122
129
|
getStringConstant(source: string, name: string): string | undefined,
|
|
123
130
|
containsTopLevelCallable(source: string, name: string): boolean,
|
|
131
|
+
extractImports(source: string): Array<ImportStmt>,
|
|
124
132
|
parseDistMetadata(content: Uint8Array): DistMetadata,
|
|
125
133
|
parseRecord(content: string): Array<RecordEntry>,
|
|
126
134
|
parseDirectUrl(content: string): DirectUrlInfo,
|
|
@@ -709,6 +709,11 @@ const I32_MAX = 2_147_483_647;
|
|
|
709
709
|
const I32_MIN = -2_147_483_648;
|
|
710
710
|
const _typeCheckValidI32 = (n) => typeof n === 'number' && n >= I32_MIN && n <= I32_MAX;
|
|
711
711
|
|
|
712
|
+
function clampGuest(i, min, max) {
|
|
713
|
+
if (i < min || i > max) throw new TypeError(`must be between ${min} and ${max}`);
|
|
714
|
+
return i;
|
|
715
|
+
}
|
|
716
|
+
|
|
712
717
|
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
713
718
|
let _fs;
|
|
714
719
|
async function fetchCompile (url) {
|
|
@@ -3521,6 +3526,7 @@ let gen = (function* _initGenerator () {
|
|
|
3521
3526
|
let postReturn4;
|
|
3522
3527
|
let postReturn5;
|
|
3523
3528
|
let postReturn6;
|
|
3529
|
+
let postReturn7;
|
|
3524
3530
|
function trampoline0(handle) {
|
|
3525
3531
|
const handleEntry = rscTableRemove(handleTable1, handle);
|
|
3526
3532
|
if (handleEntry.own) {
|
|
@@ -3716,12 +3722,13 @@ let gen = (function* _initGenerator () {
|
|
|
3716
3722
|
},
|
|
3717
3723
|
}));
|
|
3718
3724
|
postReturn0 = exports1['cabi_post_evaluate-marker'];
|
|
3719
|
-
postReturn1 = exports1['
|
|
3720
|
-
postReturn2 = exports1['cabi_post_parse-
|
|
3721
|
-
postReturn3 = exports1['cabi_post_parse-
|
|
3722
|
-
postReturn4 = exports1['
|
|
3723
|
-
postReturn5 = exports1['
|
|
3724
|
-
postReturn6 = exports1['cabi_post_parse-
|
|
3725
|
+
postReturn1 = exports1['cabi_post_extract-imports'];
|
|
3726
|
+
postReturn2 = exports1['cabi_post_parse-dist-metadata'];
|
|
3727
|
+
postReturn3 = exports1['cabi_post_parse-record'];
|
|
3728
|
+
postReturn4 = exports1['cabi_post_parse-direct-url'];
|
|
3729
|
+
postReturn5 = exports1['cabi_post_normalize-package-name'];
|
|
3730
|
+
postReturn6 = exports1['cabi_post_parse-requirements-txt'];
|
|
3731
|
+
postReturn7 = exports1['cabi_post_parse-pep508'];
|
|
3725
3732
|
let exports1FindAppOrHandler;
|
|
3726
3733
|
|
|
3727
3734
|
function findAppOrHandler(arg0) {
|
|
@@ -3841,6 +3848,77 @@ let gen = (function* _initGenerator () {
|
|
|
3841
3848
|
});
|
|
3842
3849
|
return bool2 == 0 ? false : (bool2 == 1 ? true : throwInvalidBool());
|
|
3843
3850
|
}
|
|
3851
|
+
let exports1ExtractImports;
|
|
3852
|
+
|
|
3853
|
+
function extractImports(arg0) {
|
|
3854
|
+
var ptr0 = utf8Encode(arg0, realloc0, memory0);
|
|
3855
|
+
var len0 = utf8EncodedLen;
|
|
3856
|
+
_debugLog('[iface="extract-imports", function="extract-imports"][Instruction::CallWasm] enter', {
|
|
3857
|
+
funcName: 'extract-imports',
|
|
3858
|
+
paramCount: 2,
|
|
3859
|
+
async: false,
|
|
3860
|
+
postReturn: true,
|
|
3861
|
+
});
|
|
3862
|
+
const _wasm_call_currentTaskID = startCurrentTask(0, false, 'exports1ExtractImports');
|
|
3863
|
+
const ret = exports1ExtractImports(ptr0, len0);
|
|
3864
|
+
endCurrentTask(0);
|
|
3865
|
+
var len7 = dataView(memory0).getUint32(ret + 4, true);
|
|
3866
|
+
var base7 = dataView(memory0).getUint32(ret + 0, true);
|
|
3867
|
+
var result7 = [];
|
|
3868
|
+
for (let i = 0; i < len7; i++) {
|
|
3869
|
+
const base = base7 + i * 28;
|
|
3870
|
+
let variant2;
|
|
3871
|
+
switch (dataView(memory0).getUint8(base + 0, true)) {
|
|
3872
|
+
case 0: {
|
|
3873
|
+
variant2 = undefined;
|
|
3874
|
+
break;
|
|
3875
|
+
}
|
|
3876
|
+
case 1: {
|
|
3877
|
+
var ptr1 = dataView(memory0).getUint32(base + 4, true);
|
|
3878
|
+
var len1 = dataView(memory0).getUint32(base + 8, true);
|
|
3879
|
+
var result1 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr1, len1));
|
|
3880
|
+
variant2 = result1;
|
|
3881
|
+
break;
|
|
3882
|
+
}
|
|
3883
|
+
default: {
|
|
3884
|
+
throw new TypeError('invalid variant discriminant for option');
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
var len4 = dataView(memory0).getUint32(base + 20, true);
|
|
3888
|
+
var base4 = dataView(memory0).getUint32(base + 16, true);
|
|
3889
|
+
var result4 = [];
|
|
3890
|
+
for (let i = 0; i < len4; i++) {
|
|
3891
|
+
const base = base4 + i * 8;
|
|
3892
|
+
var ptr3 = dataView(memory0).getUint32(base + 0, true);
|
|
3893
|
+
var len3 = dataView(memory0).getUint32(base + 4, true);
|
|
3894
|
+
var result3 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr3, len3));
|
|
3895
|
+
result4.push(result3);
|
|
3896
|
+
}
|
|
3897
|
+
var bool5 = dataView(memory0).getUint8(base + 24, true);
|
|
3898
|
+
var bool6 = dataView(memory0).getUint8(base + 25, true);
|
|
3899
|
+
result7.push({
|
|
3900
|
+
module: variant2,
|
|
3901
|
+
level: clampGuest(dataView(memory0).getUint8(base + 12, true), 0, 255),
|
|
3902
|
+
names: result4,
|
|
3903
|
+
isModuleLevel: bool5 == 0 ? false : (bool5 == 1 ? true : throwInvalidBool()),
|
|
3904
|
+
inTypeChecking: bool6 == 0 ? false : (bool6 == 1 ? true : throwInvalidBool()),
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
_debugLog('[iface="extract-imports", function="extract-imports"][Instruction::Return]', {
|
|
3908
|
+
funcName: 'extract-imports',
|
|
3909
|
+
paramCount: 1,
|
|
3910
|
+
async: false,
|
|
3911
|
+
postReturn: true
|
|
3912
|
+
});
|
|
3913
|
+
const retCopy = result7;
|
|
3914
|
+
|
|
3915
|
+
let cstate = getOrCreateAsyncState(0);
|
|
3916
|
+
cstate.mayLeave = false;
|
|
3917
|
+
postReturn1(ret);
|
|
3918
|
+
cstate.mayLeave = true;
|
|
3919
|
+
return retCopy;
|
|
3920
|
+
|
|
3921
|
+
}
|
|
3844
3922
|
let exports1ParseDistMetadata;
|
|
3845
3923
|
|
|
3846
3924
|
function parseDistMetadata(arg0) {
|
|
@@ -4171,7 +4249,7 @@ let gen = (function* _initGenerator () {
|
|
|
4171
4249
|
|
|
4172
4250
|
let cstate = getOrCreateAsyncState(0);
|
|
4173
4251
|
cstate.mayLeave = false;
|
|
4174
|
-
|
|
4252
|
+
postReturn2(ret);
|
|
4175
4253
|
cstate.mayLeave = true;
|
|
4176
4254
|
|
|
4177
4255
|
|
|
@@ -4274,7 +4352,7 @@ let gen = (function* _initGenerator () {
|
|
|
4274
4352
|
|
|
4275
4353
|
let cstate = getOrCreateAsyncState(0);
|
|
4276
4354
|
cstate.mayLeave = false;
|
|
4277
|
-
|
|
4355
|
+
postReturn3(ret);
|
|
4278
4356
|
cstate.mayLeave = true;
|
|
4279
4357
|
|
|
4280
4358
|
|
|
@@ -4434,7 +4512,7 @@ let gen = (function* _initGenerator () {
|
|
|
4434
4512
|
|
|
4435
4513
|
let cstate = getOrCreateAsyncState(0);
|
|
4436
4514
|
cstate.mayLeave = false;
|
|
4437
|
-
|
|
4515
|
+
postReturn4(ret);
|
|
4438
4516
|
cstate.mayLeave = true;
|
|
4439
4517
|
|
|
4440
4518
|
|
|
@@ -4472,7 +4550,7 @@ let gen = (function* _initGenerator () {
|
|
|
4472
4550
|
|
|
4473
4551
|
let cstate = getOrCreateAsyncState(0);
|
|
4474
4552
|
cstate.mayLeave = false;
|
|
4475
|
-
|
|
4553
|
+
postReturn5(ret);
|
|
4476
4554
|
cstate.mayLeave = true;
|
|
4477
4555
|
return retCopy;
|
|
4478
4556
|
|
|
@@ -4929,7 +5007,7 @@ let gen = (function* _initGenerator () {
|
|
|
4929
5007
|
|
|
4930
5008
|
let cstate = getOrCreateAsyncState(0);
|
|
4931
5009
|
cstate.mayLeave = false;
|
|
4932
|
-
|
|
5010
|
+
postReturn6(ret);
|
|
4933
5011
|
cstate.mayLeave = true;
|
|
4934
5012
|
|
|
4935
5013
|
|
|
@@ -5144,7 +5222,7 @@ let gen = (function* _initGenerator () {
|
|
|
5144
5222
|
|
|
5145
5223
|
let cstate = getOrCreateAsyncState(0);
|
|
5146
5224
|
cstate.mayLeave = false;
|
|
5147
|
-
|
|
5225
|
+
postReturn7(ret);
|
|
5148
5226
|
cstate.mayLeave = true;
|
|
5149
5227
|
|
|
5150
5228
|
|
|
@@ -5284,6 +5362,7 @@ let gen = (function* _initGenerator () {
|
|
|
5284
5362
|
exports1FindAppOrHandler = exports1['find-app-or-handler'];
|
|
5285
5363
|
exports1GetStringConstant = exports1['get-string-constant'];
|
|
5286
5364
|
exports1ContainsTopLevelCallable = exports1['contains-top-level-callable'];
|
|
5365
|
+
exports1ExtractImports = exports1['extract-imports'];
|
|
5287
5366
|
exports1ParseDistMetadata = exports1['parse-dist-metadata'];
|
|
5288
5367
|
exports1ParseRecord = exports1['parse-record'];
|
|
5289
5368
|
exports1ParseDirectUrl = exports1['parse-direct-url'];
|
|
@@ -5293,7 +5372,7 @@ let gen = (function* _initGenerator () {
|
|
|
5293
5372
|
exports1IsWheelCompatible = exports1['is-wheel-compatible'];
|
|
5294
5373
|
exports1EvaluateMarker = exports1['evaluate-marker'];
|
|
5295
5374
|
|
|
5296
|
-
return { containsTopLevelCallable, evaluateMarker, findAppOrHandler, getStringConstant, isWheelCompatible, normalizePackageName, parseDirectUrl, parseDistMetadata, parsePep508, parseRecord, parseRequirementsTxt, };
|
|
5375
|
+
return { containsTopLevelCallable, evaluateMarker, extractImports, findAppOrHandler, getStringConstant, isWheelCompatible, normalizePackageName, parseDirectUrl, parseDistMetadata, parsePep508, parseRecord, parseRequirementsTxt, };
|
|
5297
5376
|
})();
|
|
5298
5377
|
let promise, resolve, reject;
|
|
5299
5378
|
function runNext (value) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python-analysis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"main": "./dist/index.cjs",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -43,15 +43,14 @@
|
|
|
43
43
|
"@types/js-yaml": "4.0.9",
|
|
44
44
|
"@types/node": "20.11.0",
|
|
45
45
|
"ts-to-zod": "^3",
|
|
46
|
-
"vitest": "
|
|
46
|
+
"vitest": "4.1.10"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
-
"build": "
|
|
49
|
+
"build": "node scripts/build.mjs",
|
|
50
50
|
"build:wasm": "node scripts/build-wasm.mjs",
|
|
51
51
|
"generate:schemas": "ts-to-zod --all",
|
|
52
52
|
"test": "vitest run --config ../../vitest.config.mts",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"type-check": "tsc --noEmit"
|
|
53
|
+
"type-check": "tsc --noEmit",
|
|
54
|
+
"test-unit": "vitest run --config ../../vitest.config.mts"
|
|
56
55
|
}
|
|
57
56
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host-side implementation of the `vercel:python-analysis/host-utils` WIT interface.
|
|
3
|
-
*/
|
|
4
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
|
-
/**
|
|
6
|
-
* Function type for reading referenced requirement files.
|
|
7
|
-
*/
|
|
8
|
-
export type ReadFileFn = (path: string) => string | null;
|
|
9
|
-
export interface ReadFileContext {
|
|
10
|
-
readFile: ReadFileFn;
|
|
11
|
-
workingDir?: string;
|
|
12
|
-
}
|
|
13
|
-
export declare const readFileStorage: AsyncLocalStorage<ReadFileContext>;
|
|
14
|
-
export declare function createHostUtils(): {
|
|
15
|
-
readFile(path: string): string;
|
|
16
|
-
domainToAscii(domain: string): string;
|
|
17
|
-
domainToUnicode(domain: string): [string, boolean];
|
|
18
|
-
nfcNormalize(s: string): string;
|
|
19
|
-
nfdNormalize(s: string): string;
|
|
20
|
-
nfkcNormalize(s: string): string;
|
|
21
|
-
nfkdNormalize(s: string): string;
|
|
22
|
-
};
|
package/dist/wasm/load.d.ts
DELETED