@vercel/python-analysis 0.5.0-canary.20260211174907.cdd2da6 → 0.5.0
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 +253 -48
- package/dist/index.d.ts +4 -0
- package/dist/index.js +215 -16
- package/dist/manifest/dist-metadata.d.ts +103 -0
- package/dist/manifest/uv-lock-parser.d.ts +77 -0
- package/dist/util/error.d.ts +7 -1
- package/dist/wasm/interfaces/wasi-random-insecure-seed.d.ts +2 -0
- 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.core4.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.d.ts +60 -0
- package/dist/wasm/vercel_python_analysis.js +734 -20
- package/package.json +1 -1
- /package/dist/{semantic → wasm}/load.d.ts +0 -0
package/dist/index.cjs
CHANGED
|
@@ -62,15 +62,21 @@ __export(src_exports, {
|
|
|
62
62
|
UvConfigSchema: () => UvConfigSchema,
|
|
63
63
|
UvConfigWorkspaceSchema: () => UvConfigWorkspaceSchema,
|
|
64
64
|
UvIndexEntrySchema: () => UvIndexEntrySchema,
|
|
65
|
+
classifyPackages: () => classifyPackages,
|
|
65
66
|
containsAppOrHandler: () => containsAppOrHandler,
|
|
66
67
|
createMinimalManifest: () => createMinimalManifest,
|
|
67
68
|
discoverPythonPackage: () => discoverPythonPackage,
|
|
69
|
+
generateRuntimeRequirements: () => generateRuntimeRequirements,
|
|
70
|
+
isPrivatePackageSource: () => isPrivatePackageSource,
|
|
71
|
+
normalizePackageName: () => normalizePackageName,
|
|
72
|
+
parseUvLock: () => parseUvLock,
|
|
73
|
+
scanDistributions: () => scanDistributions,
|
|
68
74
|
selectPython: () => selectPython,
|
|
69
75
|
stringifyManifest: () => stringifyManifest
|
|
70
76
|
});
|
|
71
77
|
module.exports = __toCommonJS(src_exports);
|
|
72
78
|
|
|
73
|
-
// src/
|
|
79
|
+
// src/wasm/load.ts
|
|
74
80
|
var import_promises = require("fs/promises");
|
|
75
81
|
var import_node_module = require("module");
|
|
76
82
|
var import_node_path = require("path");
|
|
@@ -89,7 +95,7 @@ function getWasmDir() {
|
|
|
89
95
|
}
|
|
90
96
|
async function getCoreModule(path4) {
|
|
91
97
|
const wasmPath = (0, import_node_path.join)(getWasmDir(), path4);
|
|
92
|
-
const wasmBytes = await (0, import_promises.readFile)(wasmPath);
|
|
98
|
+
const wasmBytes = new Uint8Array(await (0, import_promises.readFile)(wasmPath));
|
|
93
99
|
return WebAssembly.compile(wasmBytes);
|
|
94
100
|
}
|
|
95
101
|
async function importWasmModule() {
|
|
@@ -119,17 +125,107 @@ async function containsAppOrHandler(source) {
|
|
|
119
125
|
return mod.containsAppOrHandler(source);
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
// src/manifest/dist-metadata.ts
|
|
129
|
+
var import_promises2 = require("fs/promises");
|
|
130
|
+
var import_node_path2 = require("path");
|
|
131
|
+
async function readDistInfoFile(distInfoDir, filename) {
|
|
132
|
+
try {
|
|
133
|
+
return await (0, import_promises2.readFile)((0, import_node_path2.join)(distInfoDir, filename), "utf-8");
|
|
134
|
+
} catch {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function scanDistributions(sitePackagesDir) {
|
|
139
|
+
const mod = await importWasmModule();
|
|
140
|
+
const index = /* @__PURE__ */ new Map();
|
|
141
|
+
let entries;
|
|
142
|
+
try {
|
|
143
|
+
entries = await (0, import_promises2.readdir)(sitePackagesDir);
|
|
144
|
+
} catch {
|
|
145
|
+
return index;
|
|
146
|
+
}
|
|
147
|
+
const distInfoDirs = entries.filter((e) => e.endsWith(".dist-info"));
|
|
148
|
+
for (const dirName of distInfoDirs) {
|
|
149
|
+
const distInfoPath = (0, import_node_path2.join)(sitePackagesDir, dirName);
|
|
150
|
+
const metadataContent = await readDistInfoFile(distInfoPath, "METADATA");
|
|
151
|
+
if (!metadataContent) {
|
|
152
|
+
console.debug(`Missing METADATA in ${dirName}`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let metadata;
|
|
156
|
+
try {
|
|
157
|
+
metadata = mod.parseDistMetadata(
|
|
158
|
+
new TextEncoder().encode(metadataContent)
|
|
159
|
+
);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.debug(`Failed to parse METADATA for ${dirName}: ${e}`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const normalizedName = mod.normalizePackageName(metadata.name);
|
|
165
|
+
let files = [];
|
|
166
|
+
const recordContent = await readDistInfoFile(distInfoPath, "RECORD");
|
|
167
|
+
if (recordContent) {
|
|
168
|
+
try {
|
|
169
|
+
files = mod.parseRecord(recordContent);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
console.warn(`Failed to parse RECORD for ${dirName}: ${e}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
let origin;
|
|
175
|
+
const directUrlContent = await readDistInfoFile(
|
|
176
|
+
distInfoPath,
|
|
177
|
+
"direct_url.json"
|
|
178
|
+
);
|
|
179
|
+
if (directUrlContent) {
|
|
180
|
+
try {
|
|
181
|
+
origin = mod.parseDirectUrl(directUrlContent);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
console.debug(`Failed to parse direct_url.json for ${dirName}: ${e}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const installerContent = await readDistInfoFile(distInfoPath, "INSTALLER");
|
|
187
|
+
const installer = installerContent?.trim() || void 0;
|
|
188
|
+
const dist = {
|
|
189
|
+
name: normalizedName,
|
|
190
|
+
version: metadata.version,
|
|
191
|
+
metadataVersion: metadata.metadataVersion,
|
|
192
|
+
summary: metadata.summary,
|
|
193
|
+
description: metadata.description,
|
|
194
|
+
descriptionContentType: metadata.descriptionContentType,
|
|
195
|
+
requiresDist: metadata.requiresDist,
|
|
196
|
+
requiresPython: metadata.requiresPython,
|
|
197
|
+
providesExtra: metadata.providesExtra,
|
|
198
|
+
author: metadata.author,
|
|
199
|
+
authorEmail: metadata.authorEmail,
|
|
200
|
+
maintainer: metadata.maintainer,
|
|
201
|
+
maintainerEmail: metadata.maintainerEmail,
|
|
202
|
+
license: metadata.license,
|
|
203
|
+
licenseExpression: metadata.licenseExpression,
|
|
204
|
+
classifiers: metadata.classifiers,
|
|
205
|
+
homePage: metadata.homePage,
|
|
206
|
+
projectUrls: metadata.projectUrls,
|
|
207
|
+
platforms: metadata.platforms,
|
|
208
|
+
dynamic: metadata.dynamic,
|
|
209
|
+
files,
|
|
210
|
+
origin,
|
|
211
|
+
installer
|
|
212
|
+
};
|
|
213
|
+
index.set(normalizedName, dist);
|
|
214
|
+
}
|
|
215
|
+
return index;
|
|
216
|
+
}
|
|
217
|
+
|
|
122
218
|
// src/manifest/package.ts
|
|
123
|
-
var
|
|
219
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
124
220
|
var import_minimatch = require("minimatch");
|
|
125
221
|
|
|
126
222
|
// src/util/config.ts
|
|
127
|
-
var
|
|
223
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
128
224
|
var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
129
225
|
var import_smol_toml = __toESM(require("smol-toml"), 1);
|
|
130
226
|
|
|
131
227
|
// src/util/fs.ts
|
|
132
|
-
var
|
|
228
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
133
229
|
var import_fs_extra = require("fs-extra");
|
|
134
230
|
|
|
135
231
|
// src/util/error.ts
|
|
@@ -138,7 +234,14 @@ var isErrnoException = (error, code = void 0) => {
|
|
|
138
234
|
return import_node_util.default.types.isNativeError(error) && "code" in error && (code === void 0 || error.code === code);
|
|
139
235
|
};
|
|
140
236
|
var PythonAnalysisError = class extends Error {
|
|
141
|
-
constructor({
|
|
237
|
+
constructor({
|
|
238
|
+
message,
|
|
239
|
+
code,
|
|
240
|
+
path: path4,
|
|
241
|
+
link,
|
|
242
|
+
action,
|
|
243
|
+
fileContent
|
|
244
|
+
}) {
|
|
142
245
|
super(message);
|
|
143
246
|
this.hideStackTrace = true;
|
|
144
247
|
this.name = "PythonAnalysisError";
|
|
@@ -146,6 +249,7 @@ var PythonAnalysisError = class extends Error {
|
|
|
146
249
|
this.path = path4;
|
|
147
250
|
this.link = link;
|
|
148
251
|
this.action = action;
|
|
252
|
+
this.fileContent = fileContent;
|
|
149
253
|
}
|
|
150
254
|
};
|
|
151
255
|
|
|
@@ -169,21 +273,21 @@ async function readFileTextIfExists(file, encoding = "utf8") {
|
|
|
169
273
|
}
|
|
170
274
|
}
|
|
171
275
|
function normalizePath(p) {
|
|
172
|
-
let np =
|
|
173
|
-
if (np.endsWith(
|
|
276
|
+
let np = import_node_path3.default.normalize(p);
|
|
277
|
+
if (np.endsWith(import_node_path3.default.sep)) {
|
|
174
278
|
np = np.slice(0, -1);
|
|
175
279
|
}
|
|
176
280
|
return np;
|
|
177
281
|
}
|
|
178
282
|
function isSubpath(somePath, parentPath) {
|
|
179
|
-
const rel =
|
|
180
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
283
|
+
const rel = import_node_path3.default.relative(parentPath, somePath);
|
|
284
|
+
return rel === "" || !rel.startsWith("..") && !import_node_path3.default.isAbsolute(rel);
|
|
181
285
|
}
|
|
182
286
|
|
|
183
287
|
// src/util/config.ts
|
|
184
288
|
function parseRawConfig(content, filename, filetype = void 0) {
|
|
185
289
|
if (filetype === void 0) {
|
|
186
|
-
filetype =
|
|
290
|
+
filetype = import_node_path4.default.extname(filename.toLowerCase());
|
|
187
291
|
}
|
|
188
292
|
try {
|
|
189
293
|
if (filetype === ".json") {
|
|
@@ -207,7 +311,8 @@ function parseRawConfig(content, filename, filetype = void 0) {
|
|
|
207
311
|
throw new PythonAnalysisError({
|
|
208
312
|
message: `Could not parse config file "${filename}": ${error.message}`,
|
|
209
313
|
code: "PYTHON_CONFIG_PARSE_ERROR",
|
|
210
|
-
path: filename
|
|
314
|
+
path: filename,
|
|
315
|
+
fileContent: content
|
|
211
316
|
});
|
|
212
317
|
}
|
|
213
318
|
throw error;
|
|
@@ -225,7 +330,8 @@ function parseConfig(content, filename, schema, filetype = void 0) {
|
|
|
225
330
|
message: `Invalid config in "${filename}":
|
|
226
331
|
${issues}`,
|
|
227
332
|
code: "PYTHON_CONFIG_VALIDATION_ERROR",
|
|
228
|
-
path: filename
|
|
333
|
+
path: filename,
|
|
334
|
+
fileContent: content
|
|
229
335
|
});
|
|
230
336
|
}
|
|
231
337
|
return result.data;
|
|
@@ -712,7 +818,7 @@ var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
|
|
|
712
818
|
var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
|
|
713
819
|
|
|
714
820
|
// src/manifest/requirements-txt-parser.ts
|
|
715
|
-
var
|
|
821
|
+
var import_node_path5 = require("path");
|
|
716
822
|
var import_pip_requirements_js = require("pip-requirements-js");
|
|
717
823
|
var PRIMARY_INDEX_NAME = "primary";
|
|
718
824
|
var EXTRA_INDEX_PREFIX = "extra-";
|
|
@@ -1050,9 +1156,9 @@ function normalizePathRequirement(rawLine) {
|
|
|
1050
1156
|
}
|
|
1051
1157
|
return req;
|
|
1052
1158
|
}
|
|
1053
|
-
function convertRequirementsToPyprojectToml(fileContent,
|
|
1159
|
+
function convertRequirementsToPyprojectToml(fileContent, readFile4) {
|
|
1054
1160
|
const pyproject = {};
|
|
1055
|
-
const parsed = parseRequirementsFile(fileContent,
|
|
1161
|
+
const parsed = parseRequirementsFile(fileContent, readFile4);
|
|
1056
1162
|
const deps = [];
|
|
1057
1163
|
const sources = {};
|
|
1058
1164
|
for (const req of parsed.requirements) {
|
|
@@ -1111,11 +1217,11 @@ function buildIndexEntries(pipOptions) {
|
|
|
1111
1217
|
}
|
|
1112
1218
|
return indexes;
|
|
1113
1219
|
}
|
|
1114
|
-
function parseRequirementsFile(fileContent,
|
|
1220
|
+
function parseRequirementsFile(fileContent, readFile4) {
|
|
1115
1221
|
const visited = /* @__PURE__ */ new Set();
|
|
1116
|
-
return parseRequirementsFileInternal(fileContent,
|
|
1222
|
+
return parseRequirementsFileInternal(fileContent, readFile4, visited);
|
|
1117
1223
|
}
|
|
1118
|
-
function parseRequirementsFileInternal(fileContent,
|
|
1224
|
+
function parseRequirementsFileInternal(fileContent, readFile4, visited) {
|
|
1119
1225
|
const { cleanedContent, options, pathRequirements, editableRequirements } = extractPipArguments(fileContent);
|
|
1120
1226
|
const hashMap = buildHashMap(fileContent);
|
|
1121
1227
|
const requirements = (0, import_pip_requirements_js.parsePipRequirementsFile)(cleanedContent);
|
|
@@ -1163,18 +1269,18 @@ function parseRequirementsFileInternal(fileContent, readFile3, visited) {
|
|
|
1163
1269
|
normalized.push(norm);
|
|
1164
1270
|
}
|
|
1165
1271
|
}
|
|
1166
|
-
if (
|
|
1272
|
+
if (readFile4) {
|
|
1167
1273
|
for (const refPath of mergedOptions.requirementFiles) {
|
|
1168
|
-
const refPathKey = (0,
|
|
1274
|
+
const refPathKey = (0, import_node_path5.normalize)(refPath);
|
|
1169
1275
|
if (visited.has(refPathKey)) {
|
|
1170
1276
|
continue;
|
|
1171
1277
|
}
|
|
1172
1278
|
visited.add(refPathKey);
|
|
1173
|
-
const refContent =
|
|
1279
|
+
const refContent = readFile4(refPath);
|
|
1174
1280
|
if (refContent != null) {
|
|
1175
1281
|
const refParsed = parseRequirementsFileInternal(
|
|
1176
1282
|
refContent,
|
|
1177
|
-
|
|
1283
|
+
readFile4,
|
|
1178
1284
|
visited
|
|
1179
1285
|
);
|
|
1180
1286
|
const existingNames = new Set(
|
|
@@ -1717,7 +1823,7 @@ async function discoverPythonPackage({
|
|
|
1717
1823
|
}) {
|
|
1718
1824
|
const entrypointPath = normalizePath(entrypointDir);
|
|
1719
1825
|
const rootPath = normalizePath(rootDir);
|
|
1720
|
-
let prefix =
|
|
1826
|
+
let prefix = import_node_path6.default.relative(rootPath, entrypointPath);
|
|
1721
1827
|
if (prefix.startsWith("..")) {
|
|
1722
1828
|
throw new PythonAnalysisError({
|
|
1723
1829
|
message: "Entrypoint directory outside of repository root",
|
|
@@ -1741,7 +1847,7 @@ async function discoverPythonPackage({
|
|
|
1741
1847
|
if (prefix === "" || prefix === ".") {
|
|
1742
1848
|
break;
|
|
1743
1849
|
}
|
|
1744
|
-
prefix =
|
|
1850
|
+
prefix = import_node_path6.default.dirname(prefix);
|
|
1745
1851
|
}
|
|
1746
1852
|
let entrypointManifest;
|
|
1747
1853
|
let workspaceManifest;
|
|
@@ -1761,8 +1867,8 @@ async function discoverPythonPackage({
|
|
|
1761
1867
|
configs = configs.filter(
|
|
1762
1868
|
(config) => Object.values(config).some(
|
|
1763
1869
|
(cfg) => cfg !== void 0 && isSubpath(
|
|
1764
|
-
|
|
1765
|
-
|
|
1870
|
+
import_node_path6.default.dirname(cfg.path),
|
|
1871
|
+
import_node_path6.default.dirname(entrypointWorkspaceManifest.path)
|
|
1766
1872
|
)
|
|
1767
1873
|
)
|
|
1768
1874
|
);
|
|
@@ -1835,9 +1941,9 @@ function findWorkspaceManifestFor(manifest, manifestStack) {
|
|
|
1835
1941
|
if (!Array.isArray(exclude)) {
|
|
1836
1942
|
exclude = [];
|
|
1837
1943
|
}
|
|
1838
|
-
const entrypointRelPath =
|
|
1839
|
-
|
|
1840
|
-
|
|
1944
|
+
const entrypointRelPath = import_node_path6.default.relative(
|
|
1945
|
+
import_node_path6.default.dirname(parentManifest.path),
|
|
1946
|
+
import_node_path6.default.dirname(manifest.path)
|
|
1841
1947
|
);
|
|
1842
1948
|
if (members.length > 0 && members.some(
|
|
1843
1949
|
(pat) => (0, import_minimatch.match)([entrypointRelPath], pat).length > 0
|
|
@@ -1872,7 +1978,7 @@ async function loadPythonManifest(root, prefix) {
|
|
|
1872
1978
|
"requirements-frozen.txt",
|
|
1873
1979
|
"requirements.txt",
|
|
1874
1980
|
"requirements.in",
|
|
1875
|
-
|
|
1981
|
+
import_node_path6.default.join("requirements", "prod.txt")
|
|
1876
1982
|
]) {
|
|
1877
1983
|
const requirementsTxtManifest = await maybeLoadRequirementsTxt(
|
|
1878
1984
|
root,
|
|
@@ -1891,14 +1997,14 @@ async function loadPythonManifest(root, prefix) {
|
|
|
1891
1997
|
return manifest;
|
|
1892
1998
|
}
|
|
1893
1999
|
async function maybeLoadLockFile(root, subdir) {
|
|
1894
|
-
const uvLockRelPath =
|
|
1895
|
-
const uvLockPath =
|
|
2000
|
+
const uvLockRelPath = import_node_path6.default.join(subdir, "uv.lock");
|
|
2001
|
+
const uvLockPath = import_node_path6.default.join(root, uvLockRelPath);
|
|
1896
2002
|
const uvLockContent = await readFileTextIfExists(uvLockPath);
|
|
1897
2003
|
if (uvLockContent != null) {
|
|
1898
2004
|
return { path: uvLockRelPath, kind: "uv.lock" /* UvLock */ };
|
|
1899
2005
|
}
|
|
1900
|
-
const pylockRelPath =
|
|
1901
|
-
const pylockPath =
|
|
2006
|
+
const pylockRelPath = import_node_path6.default.join(subdir, "pylock.toml");
|
|
2007
|
+
const pylockPath = import_node_path6.default.join(root, pylockRelPath);
|
|
1902
2008
|
const pylockContent = await readFileTextIfExists(pylockPath);
|
|
1903
2009
|
if (pylockContent != null) {
|
|
1904
2010
|
return { path: pylockRelPath, kind: "pylock.toml" /* PylockToml */ };
|
|
@@ -1906,8 +2012,8 @@ async function maybeLoadLockFile(root, subdir) {
|
|
|
1906
2012
|
return void 0;
|
|
1907
2013
|
}
|
|
1908
2014
|
async function maybeLoadPyProjectToml(root, subdir) {
|
|
1909
|
-
const pyprojectTomlRelPath =
|
|
1910
|
-
const pyprojectTomlPath =
|
|
2015
|
+
const pyprojectTomlRelPath = import_node_path6.default.join(subdir, "pyproject.toml");
|
|
2016
|
+
const pyprojectTomlPath = import_node_path6.default.join(root, pyprojectTomlRelPath);
|
|
1911
2017
|
let pyproject;
|
|
1912
2018
|
try {
|
|
1913
2019
|
pyproject = await readConfigIfExists(
|
|
@@ -1928,8 +2034,8 @@ async function maybeLoadPyProjectToml(root, subdir) {
|
|
|
1928
2034
|
if (pyproject == null) {
|
|
1929
2035
|
return null;
|
|
1930
2036
|
}
|
|
1931
|
-
const uvTomlRelPath =
|
|
1932
|
-
const uvTomlPath =
|
|
2037
|
+
const uvTomlRelPath = import_node_path6.default.join(subdir, "uv.toml");
|
|
2038
|
+
const uvTomlPath = import_node_path6.default.join(root, uvTomlRelPath);
|
|
1933
2039
|
let uvToml;
|
|
1934
2040
|
try {
|
|
1935
2041
|
uvToml = await readConfigIfExists(uvTomlPath, UvConfigSchema);
|
|
@@ -1959,8 +2065,8 @@ async function maybeLoadPyProjectToml(root, subdir) {
|
|
|
1959
2065
|
};
|
|
1960
2066
|
}
|
|
1961
2067
|
async function maybeLoadPipfile(root, subdir) {
|
|
1962
|
-
const pipfileRelPath =
|
|
1963
|
-
const pipfilePath =
|
|
2068
|
+
const pipfileRelPath = import_node_path6.default.join(subdir, "Pipfile");
|
|
2069
|
+
const pipfilePath = import_node_path6.default.join(root, pipfileRelPath);
|
|
1964
2070
|
let pipfile;
|
|
1965
2071
|
try {
|
|
1966
2072
|
pipfile = await readConfigIfExists(pipfilePath, PipfileLikeSchema, ".toml");
|
|
@@ -1989,8 +2095,8 @@ async function maybeLoadPipfile(root, subdir) {
|
|
|
1989
2095
|
};
|
|
1990
2096
|
}
|
|
1991
2097
|
async function maybeLoadPipfileLock(root, subdir) {
|
|
1992
|
-
const pipfileLockRelPath =
|
|
1993
|
-
const pipfileLockPath =
|
|
2098
|
+
const pipfileLockRelPath = import_node_path6.default.join(subdir, "Pipfile.lock");
|
|
2099
|
+
const pipfileLockPath = import_node_path6.default.join(root, pipfileLockRelPath);
|
|
1994
2100
|
let pipfileLock;
|
|
1995
2101
|
try {
|
|
1996
2102
|
pipfileLock = await readConfigIfExists(
|
|
@@ -2023,8 +2129,8 @@ async function maybeLoadPipfileLock(root, subdir) {
|
|
|
2023
2129
|
};
|
|
2024
2130
|
}
|
|
2025
2131
|
async function maybeLoadRequirementsTxt(root, subdir, fileName) {
|
|
2026
|
-
const requirementsTxtRelPath =
|
|
2027
|
-
const requirementsTxtPath =
|
|
2132
|
+
const requirementsTxtRelPath = import_node_path6.default.join(subdir, fileName);
|
|
2133
|
+
const requirementsTxtPath = import_node_path6.default.join(root, requirementsTxtRelPath);
|
|
2028
2134
|
const requirementsContent = await readFileTextIfExists(requirementsTxtPath);
|
|
2029
2135
|
if (requirementsContent == null) {
|
|
2030
2136
|
return null;
|
|
@@ -2042,12 +2148,16 @@ async function maybeLoadRequirementsTxt(root, subdir, fileName) {
|
|
|
2042
2148
|
} catch (error) {
|
|
2043
2149
|
if (error instanceof PythonAnalysisError) {
|
|
2044
2150
|
error.path = requirementsTxtRelPath;
|
|
2151
|
+
if (!error.fileContent) {
|
|
2152
|
+
error.fileContent = requirementsContent;
|
|
2153
|
+
}
|
|
2045
2154
|
throw error;
|
|
2046
2155
|
}
|
|
2047
2156
|
throw new PythonAnalysisError({
|
|
2048
2157
|
message: `could not parse ${fileName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
2049
2158
|
code: "PYTHON_REQUIREMENTS_PARSE_ERROR",
|
|
2050
|
-
path: requirementsTxtRelPath
|
|
2159
|
+
path: requirementsTxtRelPath,
|
|
2160
|
+
fileContent: requirementsContent
|
|
2051
2161
|
});
|
|
2052
2162
|
}
|
|
2053
2163
|
}
|
|
@@ -2060,8 +2170,8 @@ async function loadPythonConfigs(root, prefix) {
|
|
|
2060
2170
|
return configs;
|
|
2061
2171
|
}
|
|
2062
2172
|
async function maybeLoadPythonRequest(root, subdir) {
|
|
2063
|
-
const dotPythonVersionRelPath =
|
|
2064
|
-
const dotPythonVersionPath =
|
|
2173
|
+
const dotPythonVersionRelPath = import_node_path6.default.join(subdir, ".python-version");
|
|
2174
|
+
const dotPythonVersionPath = import_node_path6.default.join(
|
|
2065
2175
|
root,
|
|
2066
2176
|
dotPythonVersionRelPath
|
|
2067
2177
|
);
|
|
@@ -2107,6 +2217,95 @@ function createMinimalManifest(options = {}) {
|
|
|
2107
2217
|
};
|
|
2108
2218
|
}
|
|
2109
2219
|
|
|
2220
|
+
// src/manifest/uv-lock-parser.ts
|
|
2221
|
+
var import_smol_toml3 = __toESM(require("smol-toml"), 1);
|
|
2222
|
+
function parseUvLock(content, path4) {
|
|
2223
|
+
let parsed;
|
|
2224
|
+
try {
|
|
2225
|
+
parsed = import_smol_toml3.default.parse(content);
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
throw new PythonAnalysisError({
|
|
2228
|
+
message: `Could not parse uv.lock: ${error instanceof Error ? error.message : String(error)}`,
|
|
2229
|
+
code: "PYTHON_UV_LOCK_PARSE_ERROR",
|
|
2230
|
+
path: path4,
|
|
2231
|
+
fileContent: content
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
const packages = (parsed.package ?? []).filter((pkg) => pkg.name && pkg.version).map((pkg) => ({
|
|
2235
|
+
name: pkg.name,
|
|
2236
|
+
version: pkg.version,
|
|
2237
|
+
source: pkg.source
|
|
2238
|
+
}));
|
|
2239
|
+
return { version: parsed.version, packages };
|
|
2240
|
+
}
|
|
2241
|
+
var PUBLIC_PYPI_PATTERNS = [
|
|
2242
|
+
"https://pypi.org",
|
|
2243
|
+
"https://files.pythonhosted.org",
|
|
2244
|
+
"pypi.org"
|
|
2245
|
+
];
|
|
2246
|
+
function isPublicPyPIRegistry(registryUrl) {
|
|
2247
|
+
if (!registryUrl)
|
|
2248
|
+
return true;
|
|
2249
|
+
const normalized = registryUrl.toLowerCase();
|
|
2250
|
+
return PUBLIC_PYPI_PATTERNS.some((pattern) => normalized.includes(pattern));
|
|
2251
|
+
}
|
|
2252
|
+
function isPrivatePackageSource(source) {
|
|
2253
|
+
if (!source)
|
|
2254
|
+
return false;
|
|
2255
|
+
if (source.git)
|
|
2256
|
+
return true;
|
|
2257
|
+
if (source.path)
|
|
2258
|
+
return true;
|
|
2259
|
+
if (source.editable)
|
|
2260
|
+
return true;
|
|
2261
|
+
if (source.url)
|
|
2262
|
+
return true;
|
|
2263
|
+
if (source.virtual)
|
|
2264
|
+
return true;
|
|
2265
|
+
if (source.registry && !isPublicPyPIRegistry(source.registry)) {
|
|
2266
|
+
return true;
|
|
2267
|
+
}
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2270
|
+
function normalizePackageName(name) {
|
|
2271
|
+
return name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
2272
|
+
}
|
|
2273
|
+
function classifyPackages(options) {
|
|
2274
|
+
const { lockFile, excludePackages = [] } = options;
|
|
2275
|
+
const privatePackages = [];
|
|
2276
|
+
const publicPackages = [];
|
|
2277
|
+
const packageVersions = {};
|
|
2278
|
+
const excludeSet = new Set(excludePackages.map(normalizePackageName));
|
|
2279
|
+
for (const pkg of lockFile.packages) {
|
|
2280
|
+
if (excludeSet.has(normalizePackageName(pkg.name))) {
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
packageVersions[pkg.name] = pkg.version;
|
|
2284
|
+
if (isPrivatePackageSource(pkg.source)) {
|
|
2285
|
+
privatePackages.push(pkg.name);
|
|
2286
|
+
} else {
|
|
2287
|
+
publicPackages.push(pkg.name);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
return { privatePackages, publicPackages, packageVersions };
|
|
2291
|
+
}
|
|
2292
|
+
function generateRuntimeRequirements(classification) {
|
|
2293
|
+
const lines = [
|
|
2294
|
+
"# Auto-generated requirements for runtime installation",
|
|
2295
|
+
"# Private packages are bundled separately and not listed here.",
|
|
2296
|
+
""
|
|
2297
|
+
];
|
|
2298
|
+
for (const pkgName of classification.publicPackages) {
|
|
2299
|
+
const version = classification.packageVersions[pkgName];
|
|
2300
|
+
if (version) {
|
|
2301
|
+
lines.push(`${pkgName}==${version}`);
|
|
2302
|
+
} else {
|
|
2303
|
+
lines.push(pkgName);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return lines.join("\n");
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2110
2309
|
// src/manifest/python-selector.ts
|
|
2111
2310
|
function selectPython(constraints, available) {
|
|
2112
2311
|
const warnings = [];
|
|
@@ -2290,9 +2489,15 @@ var HashDigestSchema = hashDigestSchema;
|
|
|
2290
2489
|
UvConfigSchema,
|
|
2291
2490
|
UvConfigWorkspaceSchema,
|
|
2292
2491
|
UvIndexEntrySchema,
|
|
2492
|
+
classifyPackages,
|
|
2293
2493
|
containsAppOrHandler,
|
|
2294
2494
|
createMinimalManifest,
|
|
2295
2495
|
discoverPythonPackage,
|
|
2496
|
+
generateRuntimeRequirements,
|
|
2497
|
+
isPrivatePackageSource,
|
|
2498
|
+
normalizePackageName,
|
|
2499
|
+
parseUvLock,
|
|
2500
|
+
scanDistributions,
|
|
2296
2501
|
selectPython,
|
|
2297
2502
|
stringifyManifest
|
|
2298
2503
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,13 @@
|
|
|
7
7
|
* @module @vercel/python-analysis
|
|
8
8
|
*/
|
|
9
9
|
export { containsAppOrHandler } from './semantic/entrypoints';
|
|
10
|
+
export type { Distribution, DistributionIndex, PackagePath, DirectUrlInfo, } from './manifest/dist-metadata';
|
|
11
|
+
export { scanDistributions } from './manifest/dist-metadata';
|
|
10
12
|
export type { PythonConfig, PythonConfigs, PythonLockFile, PythonManifest, PythonManifestOrigin, PythonPackage, PythonVersionConfig, } from './manifest/package';
|
|
11
13
|
export { discoverPythonPackage, PythonConfigKind, PythonLockFileKind, PythonManifestConvertedKind, PythonManifestKind, } from './manifest/package';
|
|
12
14
|
export { createMinimalManifest, stringifyManifest, type CreateMinimalManifestOptions, } from './manifest/serialize';
|
|
15
|
+
export type { ClassifyPackagesOptions, PackageClassification, UvLockFile, UvLockPackage, UvLockPackageSource, } from './manifest/uv-lock-parser';
|
|
16
|
+
export { classifyPackages, generateRuntimeRequirements, isPrivatePackageSource, normalizePackageName, parseUvLock, } from './manifest/uv-lock-parser';
|
|
13
17
|
export type { PythonSelectionResult } from './manifest/python-selector';
|
|
14
18
|
export { selectPython } from './manifest/python-selector';
|
|
15
19
|
export { PythonAnalysisError } from './util/error';
|