@vercel/python-analysis 0.9.0 → 0.10.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 +223 -555
- package/dist/index.d.ts +2 -1
- package/dist/index.js +197 -530
- package/dist/manifest/requirements-txt-parser.d.ts +18 -29
- package/dist/manifest/uv-lock-parser.d.ts +7 -0
- package/dist/manifest/wheel-compat.d.ts +16 -0
- package/dist/wasm/host-utils.d.ts +23 -0
- package/dist/wasm/interfaces/vercel-python-analysis-host-utils.d.ts +4 -0
- package/dist/wasm/interfaces/wasi-clocks-wall-clock.d.ts +5 -0
- package/dist/wasm/interfaces/wasi-filesystem-preopens.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-filesystem-types.d.ts +142 -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.d.ts +34 -0
- package/dist/wasm/vercel_python_analysis.js +2746 -108
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -2,6 +2,84 @@
|
|
|
2
2
|
import { readFile } from "fs/promises";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
|
+
|
|
6
|
+
// src/wasm/host-utils.ts
|
|
7
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
8
|
+
import { normalize } from "path/posix";
|
|
9
|
+
import { domainToUnicode as nodeDomainToUnicode } from "url";
|
|
10
|
+
var readFileStorage = new AsyncLocalStorage();
|
|
11
|
+
var WitResultError = class extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.payload = message;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function createHostUtils() {
|
|
18
|
+
return {
|
|
19
|
+
readFile(path4) {
|
|
20
|
+
const ctx = readFileStorage.getStore();
|
|
21
|
+
if (ctx?.readFile) {
|
|
22
|
+
const relative2 = stripWorkingDir(path4, ctx.workingDir);
|
|
23
|
+
if (relative2 != null) {
|
|
24
|
+
const result = ctx.readFile(relative2);
|
|
25
|
+
if (result != null)
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
throw new WitResultError(`File not found: ${path4}`);
|
|
30
|
+
},
|
|
31
|
+
domainToAscii(domain) {
|
|
32
|
+
try {
|
|
33
|
+
if (/[:#?/@[\]%\\\t\n\r]/.test(domain)) {
|
|
34
|
+
throw new Error("domain contains invalid characters");
|
|
35
|
+
}
|
|
36
|
+
const url = new URL(`http://${domain}/`);
|
|
37
|
+
if (url.hostname === "" && domain !== "") {
|
|
38
|
+
throw new Error("domain resolved to empty hostname");
|
|
39
|
+
}
|
|
40
|
+
return url.hostname;
|
|
41
|
+
} catch {
|
|
42
|
+
throw { payload: `Invalid domain: ${domain}` };
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
domainToUnicode(domain) {
|
|
46
|
+
const result = nodeDomainToUnicode(domain);
|
|
47
|
+
if (result !== "" || domain === "") {
|
|
48
|
+
return [result, true];
|
|
49
|
+
}
|
|
50
|
+
return [domain, false];
|
|
51
|
+
},
|
|
52
|
+
nfcNormalize(s) {
|
|
53
|
+
return s.normalize("NFC");
|
|
54
|
+
},
|
|
55
|
+
nfdNormalize(s) {
|
|
56
|
+
return s.normalize("NFD");
|
|
57
|
+
},
|
|
58
|
+
nfkcNormalize(s) {
|
|
59
|
+
return s.normalize("NFKC");
|
|
60
|
+
},
|
|
61
|
+
nfkdNormalize(s) {
|
|
62
|
+
return s.normalize("NFKD");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function stripWorkingDir(path4, workingDir) {
|
|
67
|
+
const normalized = normalize(path4);
|
|
68
|
+
if (!workingDir) {
|
|
69
|
+
return normalized;
|
|
70
|
+
}
|
|
71
|
+
const normalizedDir = normalize(workingDir);
|
|
72
|
+
const prefix = normalizedDir.endsWith("/") ? normalizedDir : normalizedDir + "/";
|
|
73
|
+
if (normalized.startsWith(prefix)) {
|
|
74
|
+
return normalized.slice(prefix.length);
|
|
75
|
+
}
|
|
76
|
+
if (normalized === normalizedDir) {
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/wasm/load.ts
|
|
5
83
|
var WASI_SHIM_PATH = "@bytecodealliance/preview2-shim/instantiation";
|
|
6
84
|
var WASM_MODULE_PATH = "#wasm/vercel_python_analysis.js";
|
|
7
85
|
var wasmInstance = null;
|
|
@@ -29,7 +107,10 @@ async function importWasmModule() {
|
|
|
29
107
|
const wasiShimModule = await import(WASI_SHIM_PATH);
|
|
30
108
|
const WASIShim = wasiShimModule.WASIShim;
|
|
31
109
|
const wasmModule = await import(WASM_MODULE_PATH);
|
|
32
|
-
const imports =
|
|
110
|
+
const imports = {
|
|
111
|
+
...new WASIShim().getImportObject(),
|
|
112
|
+
"vercel:python-analysis/host-utils": createHostUtils()
|
|
113
|
+
};
|
|
33
114
|
const instance = await wasmModule.instantiate(getCoreModule, imports);
|
|
34
115
|
wasmInstance = instance;
|
|
35
116
|
return instance;
|
|
@@ -824,356 +905,98 @@ var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
|
|
|
824
905
|
var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
|
|
825
906
|
|
|
826
907
|
// src/manifest/requirements-txt-parser.ts
|
|
827
|
-
import {
|
|
828
|
-
import { parsePipRequirementsFile } from "pip-requirements-js";
|
|
908
|
+
import { isAbsolute, join as join3, relative } from "path/posix";
|
|
829
909
|
var PRIMARY_INDEX_NAME = "primary";
|
|
830
910
|
var EXTRA_INDEX_PREFIX = "extra-";
|
|
831
911
|
var FIND_LINKS_PREFIX = "find-links-";
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
const fragment = remaining.slice(fragmentIdx + 1);
|
|
841
|
-
remaining = remaining.slice(0, fragmentIdx);
|
|
842
|
-
for (const part of fragment.split("&")) {
|
|
843
|
-
const [key, value] = part.split("=");
|
|
844
|
-
if (key === "egg" && value) {
|
|
845
|
-
egg = value;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
let ref;
|
|
850
|
-
const lastSlashIdx = remaining.lastIndexOf("/");
|
|
851
|
-
const atIdx = remaining.indexOf("@", lastSlashIdx > 0 ? lastSlashIdx : 0);
|
|
852
|
-
if (atIdx !== -1 && atIdx > remaining.indexOf("://")) {
|
|
853
|
-
ref = remaining.slice(atIdx + 1);
|
|
854
|
-
remaining = remaining.slice(0, atIdx);
|
|
912
|
+
async function parseWithWasm(content, readFile4, workingDir) {
|
|
913
|
+
const wasm = await importWasmModule();
|
|
914
|
+
const resolvedDir = workingDir ?? "/";
|
|
915
|
+
if (readFile4) {
|
|
916
|
+
return readFileStorage.run(
|
|
917
|
+
{ readFile: readFile4, workingDir: resolvedDir },
|
|
918
|
+
() => wasm.parseRequirementsTxt(content, resolvedDir, void 0)
|
|
919
|
+
);
|
|
855
920
|
}
|
|
856
|
-
return
|
|
857
|
-
url: remaining,
|
|
858
|
-
ref,
|
|
859
|
-
egg
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
function isGitUrl(url) {
|
|
863
|
-
return url.startsWith("git+");
|
|
921
|
+
return wasm.parseRequirementsTxt(content, workingDir ?? void 0, void 0);
|
|
864
922
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
const cleanedLines = [];
|
|
873
|
-
const pathRequirements = [];
|
|
874
|
-
const editableRequirements = [];
|
|
875
|
-
for (let i = 0; i < lines.length; i++) {
|
|
876
|
-
const line = lines[i];
|
|
877
|
-
const trimmed = line.trim();
|
|
878
|
-
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
879
|
-
cleanedLines.push(line);
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
let fullLine = trimmed;
|
|
883
|
-
let linesConsumed = 0;
|
|
884
|
-
while (fullLine.endsWith("\\") && i + linesConsumed + 1 < lines.length) {
|
|
885
|
-
linesConsumed++;
|
|
886
|
-
fullLine = fullLine.slice(0, -1) + lines[i + linesConsumed].trim();
|
|
887
|
-
}
|
|
888
|
-
const extracted = tryExtractPipArgument(fullLine, options);
|
|
889
|
-
if (extracted === true) {
|
|
890
|
-
i += linesConsumed;
|
|
891
|
-
} else if (typeof extracted === "object" && extracted.editable) {
|
|
892
|
-
editableRequirements.push(extracted.editable);
|
|
893
|
-
i += linesConsumed;
|
|
894
|
-
} else {
|
|
895
|
-
if (/^-[a-zA-Z]/.test(fullLine) && !fullLine.startsWith("-r") && !fullLine.startsWith("-c")) {
|
|
896
|
-
i += linesConsumed;
|
|
897
|
-
continue;
|
|
898
|
-
}
|
|
899
|
-
const strippedLine = stripInlineHashes(fullLine);
|
|
900
|
-
const effectiveLine = (strippedLine !== fullLine ? strippedLine : fullLine).trim();
|
|
901
|
-
if (isPathOrUrlRequirement(effectiveLine)) {
|
|
902
|
-
pathRequirements.push(effectiveLine);
|
|
903
|
-
} else if (strippedLine !== fullLine) {
|
|
904
|
-
cleanedLines.push(strippedLine);
|
|
905
|
-
} else {
|
|
906
|
-
cleanedLines.push(line);
|
|
907
|
-
for (let j = 1; j <= linesConsumed; j++) {
|
|
908
|
-
cleanedLines.push(lines[i + j]);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
i += linesConsumed;
|
|
923
|
+
function wasmEntryToNormalized(entry, editable) {
|
|
924
|
+
let name = entry.name || "";
|
|
925
|
+
if (!name && entry.url) {
|
|
926
|
+
const urlPath = entry.url.replace(/^file:\/\//, "");
|
|
927
|
+
const basename = urlPath.replace(/\/$/, "").split("/").pop();
|
|
928
|
+
if (basename && !basename.includes(".")) {
|
|
929
|
+
name = basename;
|
|
912
930
|
}
|
|
913
931
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
options,
|
|
917
|
-
pathRequirements,
|
|
918
|
-
editableRequirements
|
|
932
|
+
const req = {
|
|
933
|
+
name
|
|
919
934
|
};
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (line.startsWith("--requirement")) {
|
|
923
|
-
const path4 = extractArgValue(line, "--requirement");
|
|
924
|
-
if (path4) {
|
|
925
|
-
options.requirementFiles.push(path4);
|
|
926
|
-
return true;
|
|
927
|
-
}
|
|
935
|
+
if (entry.versionSpec) {
|
|
936
|
+
req.version = entry.versionSpec;
|
|
928
937
|
}
|
|
929
|
-
if (
|
|
930
|
-
|
|
931
|
-
if (path4) {
|
|
932
|
-
options.constraintFiles.push(path4);
|
|
933
|
-
return true;
|
|
934
|
-
}
|
|
938
|
+
if (entry.extras.length > 0) {
|
|
939
|
+
req.extras = entry.extras;
|
|
935
940
|
}
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
if (url) {
|
|
939
|
-
options.indexUrl = url;
|
|
940
|
-
return true;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
if (line.startsWith("-i ") || line === "-i") {
|
|
944
|
-
const match = line.match(/^-i\s+(\S+)/);
|
|
945
|
-
if (match) {
|
|
946
|
-
options.indexUrl = match[1];
|
|
947
|
-
return true;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
if (line.startsWith("--extra-index-url")) {
|
|
951
|
-
const url = extractArgValue(line, "--extra-index-url");
|
|
952
|
-
if (url) {
|
|
953
|
-
options.extraIndexUrls.push(url);
|
|
954
|
-
return true;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (line.startsWith("--editable")) {
|
|
958
|
-
const path4 = extractArgValue(line, "--editable");
|
|
959
|
-
if (path4) {
|
|
960
|
-
return { editable: path4 };
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
if (line.startsWith("-e ") || line.startsWith("-e ")) {
|
|
964
|
-
const path4 = line.slice(2).trim();
|
|
965
|
-
if (path4) {
|
|
966
|
-
return { editable: path4 };
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
if (line.startsWith("--find-links")) {
|
|
970
|
-
const url = extractArgValue(line, "--find-links");
|
|
971
|
-
if (url) {
|
|
972
|
-
if (!options.findLinks)
|
|
973
|
-
options.findLinks = [];
|
|
974
|
-
options.findLinks.push(url);
|
|
975
|
-
return true;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
if (line.startsWith("-f ") || line.startsWith("-f ")) {
|
|
979
|
-
const match = line.match(/^-f\s+(\S+)/);
|
|
980
|
-
if (match) {
|
|
981
|
-
if (!options.findLinks)
|
|
982
|
-
options.findLinks = [];
|
|
983
|
-
options.findLinks.push(match[1]);
|
|
984
|
-
return true;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
if (line === "--no-index" || line.startsWith("--no-index ")) {
|
|
988
|
-
options.noIndex = true;
|
|
989
|
-
return true;
|
|
990
|
-
}
|
|
991
|
-
if (line.startsWith("--no-binary") || line.startsWith("--only-binary")) {
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
if (line.startsWith("--")) {
|
|
995
|
-
return true;
|
|
996
|
-
}
|
|
997
|
-
return false;
|
|
998
|
-
}
|
|
999
|
-
function stripInlineHashes(line) {
|
|
1000
|
-
return line.replace(/\s+--hash=\S+/g, "").trim();
|
|
1001
|
-
}
|
|
1002
|
-
function extractInlineHashes(line) {
|
|
1003
|
-
const hashes = [];
|
|
1004
|
-
const hashRegex = /--hash=(\S+)/g;
|
|
1005
|
-
let match;
|
|
1006
|
-
while ((match = hashRegex.exec(line)) != null) {
|
|
1007
|
-
hashes.push(match[1]);
|
|
1008
|
-
}
|
|
1009
|
-
return hashes;
|
|
1010
|
-
}
|
|
1011
|
-
function extractArgValue(line, option) {
|
|
1012
|
-
let value = null;
|
|
1013
|
-
if (line.startsWith(`${option}=`)) {
|
|
1014
|
-
value = line.slice(option.length + 1).trim();
|
|
1015
|
-
} else if (line.startsWith(`${option} `) || line.startsWith(`${option} `)) {
|
|
1016
|
-
value = line.slice(option.length).trim();
|
|
1017
|
-
}
|
|
1018
|
-
if (!value)
|
|
1019
|
-
return null;
|
|
1020
|
-
const commentIdx = value.indexOf(" #");
|
|
1021
|
-
if (commentIdx !== -1) {
|
|
1022
|
-
value = value.slice(0, commentIdx).trim();
|
|
1023
|
-
}
|
|
1024
|
-
return value || null;
|
|
1025
|
-
}
|
|
1026
|
-
function isPathOrUrlRequirement(line) {
|
|
1027
|
-
if (line.startsWith("./") || line.startsWith("../"))
|
|
1028
|
-
return true;
|
|
1029
|
-
if (line.startsWith("/"))
|
|
1030
|
-
return true;
|
|
1031
|
-
if (line.startsWith("~/"))
|
|
1032
|
-
return true;
|
|
1033
|
-
if (/^(https?|ftp|file):\/\//i.test(line))
|
|
1034
|
-
return true;
|
|
1035
|
-
if (isBareArchiveFilename(line))
|
|
1036
|
-
return true;
|
|
1037
|
-
return false;
|
|
1038
|
-
}
|
|
1039
|
-
function isBareArchiveFilename(line) {
|
|
1040
|
-
if (line.includes(" @ "))
|
|
1041
|
-
return false;
|
|
1042
|
-
let check = line;
|
|
1043
|
-
const commentIdx = check.indexOf(" #");
|
|
1044
|
-
if (commentIdx !== -1)
|
|
1045
|
-
check = check.slice(0, commentIdx);
|
|
1046
|
-
const markerIdx = check.indexOf(" ;");
|
|
1047
|
-
if (markerIdx !== -1)
|
|
1048
|
-
check = check.slice(0, markerIdx);
|
|
1049
|
-
const extrasIdx = check.indexOf("[");
|
|
1050
|
-
if (extrasIdx !== -1)
|
|
1051
|
-
check = check.slice(0, extrasIdx);
|
|
1052
|
-
check = check.trim();
|
|
1053
|
-
return /\.(whl|tar\.gz|tar\.bz2|tar\.xz|zip)$/i.test(check);
|
|
1054
|
-
}
|
|
1055
|
-
function parseWheelFilename(filename) {
|
|
1056
|
-
if (!filename.endsWith(".whl"))
|
|
1057
|
-
return null;
|
|
1058
|
-
const stem = filename.slice(0, -4);
|
|
1059
|
-
const parts = stem.split("-");
|
|
1060
|
-
if (parts.length < 5 || parts.length > 6)
|
|
1061
|
-
return null;
|
|
1062
|
-
const name = parts[0];
|
|
1063
|
-
const version = parts[1];
|
|
1064
|
-
if (!name || !version)
|
|
1065
|
-
return null;
|
|
1066
|
-
return {
|
|
1067
|
-
name: name.replace(/_/g, "-"),
|
|
1068
|
-
version
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
function parseSdistFilename(filename) {
|
|
1072
|
-
let stem = filename;
|
|
1073
|
-
for (const ext of [".tar.gz", ".tar.bz2", ".tar.xz", ".zip"]) {
|
|
1074
|
-
if (stem.endsWith(ext)) {
|
|
1075
|
-
stem = stem.slice(0, -ext.length);
|
|
1076
|
-
break;
|
|
1077
|
-
}
|
|
941
|
+
if (entry.markers) {
|
|
942
|
+
req.markers = entry.markers;
|
|
1078
943
|
}
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
versionIdx = i;
|
|
1086
|
-
break;
|
|
944
|
+
if (entry.vcs) {
|
|
945
|
+
const source = {
|
|
946
|
+
git: entry.vcs.url
|
|
947
|
+
};
|
|
948
|
+
if (entry.vcs.rev) {
|
|
949
|
+
source.rev = entry.vcs.rev;
|
|
1087
950
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
return null;
|
|
1091
|
-
return {
|
|
1092
|
-
name: parts.slice(0, versionIdx).join("-"),
|
|
1093
|
-
version: parts.slice(versionIdx).join("-")
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
function normalizePathRequirement(rawLine) {
|
|
1097
|
-
let line = rawLine;
|
|
1098
|
-
const commentIdx = line.indexOf(" #");
|
|
1099
|
-
if (commentIdx !== -1) {
|
|
1100
|
-
line = line.slice(0, commentIdx).trim();
|
|
1101
|
-
}
|
|
1102
|
-
let markers;
|
|
1103
|
-
const markerIdx = line.indexOf(" ;");
|
|
1104
|
-
if (markerIdx !== -1) {
|
|
1105
|
-
markers = line.slice(markerIdx + 2).trim();
|
|
1106
|
-
line = line.slice(0, markerIdx).trim();
|
|
1107
|
-
}
|
|
1108
|
-
let extras;
|
|
1109
|
-
const extrasMatch = line.match(/\[([^\]]+)\]$/);
|
|
1110
|
-
if (extrasMatch) {
|
|
1111
|
-
extras = extrasMatch[1].split(",").map((e) => e.trim());
|
|
1112
|
-
line = line.slice(0, extrasMatch.index).trim();
|
|
1113
|
-
}
|
|
1114
|
-
const isUrl = /^(https?|ftp|file):\/\//i.test(line);
|
|
1115
|
-
let filename;
|
|
1116
|
-
if (isUrl) {
|
|
1117
|
-
try {
|
|
1118
|
-
const url = new URL(line);
|
|
1119
|
-
filename = url.pathname.split("/").pop() || "";
|
|
1120
|
-
} catch {
|
|
1121
|
-
return null;
|
|
951
|
+
if (editable) {
|
|
952
|
+
source.editable = true;
|
|
1122
953
|
}
|
|
1123
|
-
|
|
1124
|
-
const cleanPath = line.replace(/\/+$/, "");
|
|
1125
|
-
filename = cleanPath.split("/").pop() || "";
|
|
954
|
+
req.source = source;
|
|
1126
955
|
}
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
version = wheelParsed.version;
|
|
1135
|
-
}
|
|
1136
|
-
if (!name) {
|
|
1137
|
-
const sdistParsed = parseSdistFilename(filename);
|
|
1138
|
-
if (sdistParsed) {
|
|
1139
|
-
name = sdistParsed.name;
|
|
1140
|
-
version = sdistParsed.version;
|
|
956
|
+
if (entry.url) {
|
|
957
|
+
if (entry.url.startsWith("file://") && !entry.vcs) {
|
|
958
|
+
const given = entry.givenUrl ?? entry.url;
|
|
959
|
+
const path4 = given.startsWith("file://") ? given.slice("file://".length) : given;
|
|
960
|
+
req.source = { path: path4, ...editable ? { editable: true } : {} };
|
|
961
|
+
} else {
|
|
962
|
+
req.url = entry.url;
|
|
1141
963
|
}
|
|
1142
964
|
}
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
if (!name)
|
|
1147
|
-
return null;
|
|
1148
|
-
const req = { name };
|
|
1149
|
-
if (version) {
|
|
1150
|
-
req.version = `==${version}`;
|
|
1151
|
-
}
|
|
1152
|
-
if (extras && extras.length > 0) {
|
|
1153
|
-
req.extras = extras;
|
|
1154
|
-
}
|
|
1155
|
-
if (markers) {
|
|
1156
|
-
req.markers = markers;
|
|
1157
|
-
}
|
|
1158
|
-
if (isUrl) {
|
|
1159
|
-
req.url = line;
|
|
1160
|
-
} else {
|
|
1161
|
-
req.source = { path: line };
|
|
965
|
+
if (entry.hashes.length > 0) {
|
|
966
|
+
req.hashes = entry.hashes;
|
|
1162
967
|
}
|
|
1163
968
|
return req;
|
|
1164
969
|
}
|
|
1165
|
-
function
|
|
970
|
+
function rebasePath(p, workingDir, packageRoot) {
|
|
971
|
+
if (!workingDir || !packageRoot || workingDir === packageRoot)
|
|
972
|
+
return p;
|
|
973
|
+
if (isAbsolute(p))
|
|
974
|
+
return p;
|
|
975
|
+
const abs = join3(workingDir, p);
|
|
976
|
+
const rel = relative(packageRoot, abs);
|
|
977
|
+
if (isAbsolute(rel))
|
|
978
|
+
return rel;
|
|
979
|
+
if (rel.startsWith(".."))
|
|
980
|
+
return rel;
|
|
981
|
+
return "./" + rel;
|
|
982
|
+
}
|
|
983
|
+
async function convertRequirementsToPyprojectToml(fileContent, options) {
|
|
1166
984
|
const pyproject = {};
|
|
1167
|
-
const parsed = parseRequirementsFile(fileContent,
|
|
985
|
+
const parsed = await parseRequirementsFile(fileContent, options);
|
|
1168
986
|
const deps = [];
|
|
1169
987
|
const sources = {};
|
|
988
|
+
const { workingDir, packageRoot } = options ?? {};
|
|
1170
989
|
for (const req of parsed.requirements) {
|
|
1171
990
|
deps.push(formatPep508(req));
|
|
1172
991
|
if (req.source) {
|
|
992
|
+
const source = { ...req.source };
|
|
993
|
+
if (source.path) {
|
|
994
|
+
source.path = rebasePath(source.path, workingDir, packageRoot);
|
|
995
|
+
}
|
|
1173
996
|
if (Object.prototype.hasOwnProperty.call(sources, req.name)) {
|
|
1174
|
-
sources[req.name].push(
|
|
997
|
+
sources[req.name].push(source);
|
|
1175
998
|
} else {
|
|
1176
|
-
sources[req.name] = [
|
|
999
|
+
sources[req.name] = [source];
|
|
1177
1000
|
}
|
|
1178
1001
|
}
|
|
1179
1002
|
}
|
|
@@ -1223,216 +1046,38 @@ function buildIndexEntries(pipOptions) {
|
|
|
1223
1046
|
}
|
|
1224
1047
|
return indexes;
|
|
1225
1048
|
}
|
|
1226
|
-
function parseRequirementsFile(fileContent,
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1049
|
+
async function parseRequirementsFile(fileContent, options) {
|
|
1050
|
+
const wasmResult = await parseWithWasm(
|
|
1051
|
+
fileContent,
|
|
1052
|
+
options?.readFile,
|
|
1053
|
+
options?.workingDir
|
|
1054
|
+
);
|
|
1055
|
+
return processWasmResult(wasmResult);
|
|
1229
1056
|
}
|
|
1230
|
-
function
|
|
1231
|
-
const { cleanedContent, options, pathRequirements, editableRequirements } = extractPipArguments(fileContent);
|
|
1232
|
-
const hashMap = buildHashMap(fileContent);
|
|
1233
|
-
const requirements = parsePipRequirementsFile(cleanedContent);
|
|
1057
|
+
function processWasmResult(wasmResult) {
|
|
1234
1058
|
const normalized = [];
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1059
|
+
for (const entry of wasmResult.requirements) {
|
|
1060
|
+
normalized.push(wasmEntryToNormalized(entry, false));
|
|
1061
|
+
}
|
|
1062
|
+
for (const entry of wasmResult.editables) {
|
|
1063
|
+
const norm = wasmEntryToNormalized(entry, true);
|
|
1064
|
+
if (!norm.source && !entry.vcs) {
|
|
1065
|
+
norm.source = { path: entry.pep508, editable: true };
|
|
1066
|
+
} else if (norm.source && !norm.source.editable) {
|
|
1067
|
+
norm.source.editable = true;
|
|
1068
|
+
}
|
|
1069
|
+
normalized.push(norm);
|
|
1070
|
+
}
|
|
1071
|
+
const pipOptions = {
|
|
1072
|
+
indexUrl: wasmResult.indexUrl || void 0,
|
|
1073
|
+
extraIndexUrls: [...wasmResult.extraIndexUrls],
|
|
1074
|
+
findLinks: wasmResult.findLinks.length > 0 ? [...wasmResult.findLinks] : void 0,
|
|
1075
|
+
noIndex: wasmResult.noIndex || void 0
|
|
1242
1076
|
};
|
|
1243
|
-
for (const req of requirements) {
|
|
1244
|
-
if (req.type === "RequirementsFile") {
|
|
1245
|
-
mergedOptions.requirementFiles.push(req.path);
|
|
1246
|
-
continue;
|
|
1247
|
-
}
|
|
1248
|
-
if (req.type === "ConstraintsFile") {
|
|
1249
|
-
mergedOptions.constraintFiles.push(req.path);
|
|
1250
|
-
continue;
|
|
1251
|
-
}
|
|
1252
|
-
const norm = normalizeRequirement(req);
|
|
1253
|
-
if (norm != null) {
|
|
1254
|
-
const hashes = hashMap.get(norm.name.toLowerCase());
|
|
1255
|
-
if (hashes && hashes.length > 0) {
|
|
1256
|
-
norm.hashes = hashes;
|
|
1257
|
-
}
|
|
1258
|
-
normalized.push(norm);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
for (const rawPath of pathRequirements) {
|
|
1262
|
-
const norm = normalizePathRequirement(rawPath);
|
|
1263
|
-
if (norm != null) {
|
|
1264
|
-
normalized.push(norm);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
for (const rawPath of editableRequirements) {
|
|
1268
|
-
const norm = normalizePathRequirement(rawPath);
|
|
1269
|
-
if (norm != null) {
|
|
1270
|
-
if (norm.source) {
|
|
1271
|
-
norm.source.editable = true;
|
|
1272
|
-
} else {
|
|
1273
|
-
norm.source = { path: rawPath, editable: true };
|
|
1274
|
-
}
|
|
1275
|
-
normalized.push(norm);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
if (readFile4) {
|
|
1279
|
-
for (const refPath of mergedOptions.requirementFiles) {
|
|
1280
|
-
const refPathKey = normalize(refPath);
|
|
1281
|
-
if (visited.has(refPathKey)) {
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
visited.add(refPathKey);
|
|
1285
|
-
const refContent = readFile4(refPath);
|
|
1286
|
-
if (refContent != null) {
|
|
1287
|
-
const refParsed = parseRequirementsFileInternal(
|
|
1288
|
-
refContent,
|
|
1289
|
-
readFile4,
|
|
1290
|
-
visited
|
|
1291
|
-
);
|
|
1292
|
-
const existingNames = new Set(
|
|
1293
|
-
normalized.map((r) => r.name.toLowerCase())
|
|
1294
|
-
);
|
|
1295
|
-
for (const req of refParsed.requirements) {
|
|
1296
|
-
if (!existingNames.has(req.name.toLowerCase())) {
|
|
1297
|
-
normalized.push(req);
|
|
1298
|
-
existingNames.add(req.name.toLowerCase());
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
if (refParsed.pipOptions.indexUrl) {
|
|
1302
|
-
mergedOptions.indexUrl = refParsed.pipOptions.indexUrl;
|
|
1303
|
-
}
|
|
1304
|
-
for (const url of refParsed.pipOptions.extraIndexUrls) {
|
|
1305
|
-
if (!mergedOptions.extraIndexUrls.includes(url)) {
|
|
1306
|
-
mergedOptions.extraIndexUrls.push(url);
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
for (const constraintPath of refParsed.pipOptions.constraintFiles) {
|
|
1310
|
-
if (!mergedOptions.constraintFiles.includes(constraintPath)) {
|
|
1311
|
-
mergedOptions.constraintFiles.push(constraintPath);
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
if (refParsed.pipOptions.findLinks) {
|
|
1315
|
-
if (!mergedOptions.findLinks)
|
|
1316
|
-
mergedOptions.findLinks = [];
|
|
1317
|
-
for (const fl of refParsed.pipOptions.findLinks) {
|
|
1318
|
-
if (!mergedOptions.findLinks.includes(fl)) {
|
|
1319
|
-
mergedOptions.findLinks.push(fl);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
if (refParsed.pipOptions.noIndex) {
|
|
1324
|
-
mergedOptions.noIndex = true;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
1077
|
return {
|
|
1330
1078
|
requirements: normalized,
|
|
1331
|
-
pipOptions
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
function buildHashMap(fileContent) {
|
|
1335
|
-
const hashMap = /* @__PURE__ */ new Map();
|
|
1336
|
-
const lines = fileContent.split(/\r?\n/);
|
|
1337
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1338
|
-
let line = lines[i].trim();
|
|
1339
|
-
if (line === "" || line.startsWith("#") || line.startsWith("-")) {
|
|
1340
|
-
continue;
|
|
1341
|
-
}
|
|
1342
|
-
while (line.endsWith("\\") && i + 1 < lines.length) {
|
|
1343
|
-
i++;
|
|
1344
|
-
line = line.slice(0, -1) + lines[i].trim();
|
|
1345
|
-
}
|
|
1346
|
-
const hashes = extractInlineHashes(line);
|
|
1347
|
-
if (hashes.length === 0) {
|
|
1348
|
-
continue;
|
|
1349
|
-
}
|
|
1350
|
-
const packageMatch = line.match(/^([a-zA-Z0-9][-a-zA-Z0-9._]*)/);
|
|
1351
|
-
if (packageMatch) {
|
|
1352
|
-
const packageName = packageMatch[1].toLowerCase();
|
|
1353
|
-
hashMap.set(packageName, hashes);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
return hashMap;
|
|
1357
|
-
}
|
|
1358
|
-
function normalizeRequirement(req) {
|
|
1359
|
-
if (req.type === "RequirementsFile" || req.type === "ConstraintsFile") {
|
|
1360
|
-
return null;
|
|
1361
|
-
}
|
|
1362
|
-
if (req.type === "ProjectURL") {
|
|
1363
|
-
return normalizeProjectURLRequirement(req);
|
|
1364
|
-
}
|
|
1365
|
-
if (req.type === "ProjectName") {
|
|
1366
|
-
return normalizeProjectNameRequirement(req);
|
|
1367
|
-
}
|
|
1368
|
-
return null;
|
|
1369
|
-
}
|
|
1370
|
-
function normalizeProjectNameRequirement(req) {
|
|
1371
|
-
const normalized = {
|
|
1372
|
-
name: req.name
|
|
1373
|
-
};
|
|
1374
|
-
if (req.extras && req.extras.length > 0) {
|
|
1375
|
-
normalized.extras = req.extras;
|
|
1376
|
-
}
|
|
1377
|
-
if (req.versionSpec && req.versionSpec.length > 0) {
|
|
1378
|
-
normalized.version = req.versionSpec.map((spec) => `${spec.operator}${spec.version}`).join(",");
|
|
1379
|
-
}
|
|
1380
|
-
if (req.environmentMarkerTree) {
|
|
1381
|
-
normalized.markers = formatEnvironmentMarkers(req.environmentMarkerTree);
|
|
1382
|
-
}
|
|
1383
|
-
return normalized;
|
|
1384
|
-
}
|
|
1385
|
-
function normalizeProjectURLRequirement(req) {
|
|
1386
|
-
const normalized = {
|
|
1387
|
-
name: req.name
|
|
1079
|
+
pipOptions
|
|
1388
1080
|
};
|
|
1389
|
-
if (req.extras && req.extras.length > 0) {
|
|
1390
|
-
normalized.extras = req.extras;
|
|
1391
|
-
}
|
|
1392
|
-
if (req.environmentMarkerTree) {
|
|
1393
|
-
normalized.markers = formatEnvironmentMarkers(req.environmentMarkerTree);
|
|
1394
|
-
}
|
|
1395
|
-
if (isGitUrl(req.url)) {
|
|
1396
|
-
const parsed = parseGitUrl(req.url);
|
|
1397
|
-
if (parsed) {
|
|
1398
|
-
const source = {
|
|
1399
|
-
git: parsed.url
|
|
1400
|
-
};
|
|
1401
|
-
if (parsed.ref) {
|
|
1402
|
-
source.rev = parsed.ref;
|
|
1403
|
-
}
|
|
1404
|
-
if (parsed.editable) {
|
|
1405
|
-
source.editable = true;
|
|
1406
|
-
}
|
|
1407
|
-
normalized.source = source;
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
normalized.url = req.url;
|
|
1411
|
-
return normalized;
|
|
1412
|
-
}
|
|
1413
|
-
function formatEnvironmentMarkers(marker) {
|
|
1414
|
-
if (isEnvironmentMarkerNode(marker)) {
|
|
1415
|
-
const left = formatEnvironmentMarkers(marker.left);
|
|
1416
|
-
const right = formatEnvironmentMarkers(marker.right);
|
|
1417
|
-
return `(${left}) ${marker.operator} (${right})`;
|
|
1418
|
-
}
|
|
1419
|
-
const leaf = marker;
|
|
1420
|
-
const leftStr = formatMarkerValue(leaf.left);
|
|
1421
|
-
const rightStr = formatMarkerValue(leaf.right);
|
|
1422
|
-
return `${leftStr} ${leaf.operator} ${rightStr}`;
|
|
1423
|
-
}
|
|
1424
|
-
function isEnvironmentMarkerNode(marker) {
|
|
1425
|
-
if (typeof marker !== "object" || marker == null) {
|
|
1426
|
-
return false;
|
|
1427
|
-
}
|
|
1428
|
-
const op = marker.operator;
|
|
1429
|
-
return op === "and" || op === "or";
|
|
1430
|
-
}
|
|
1431
|
-
function formatMarkerValue(value) {
|
|
1432
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1433
|
-
return value;
|
|
1434
|
-
}
|
|
1435
|
-
return value;
|
|
1436
1081
|
}
|
|
1437
1082
|
|
|
1438
1083
|
// src/manifest/uv-config/schema.ts
|
|
@@ -2151,7 +1796,13 @@ async function maybeLoadRequirementsTxt(root, subdir, fileName) {
|
|
|
2151
1796
|
return null;
|
|
2152
1797
|
}
|
|
2153
1798
|
try {
|
|
2154
|
-
const pyproject = convertRequirementsToPyprojectToml(
|
|
1799
|
+
const pyproject = await convertRequirementsToPyprojectToml(
|
|
1800
|
+
requirementsContent,
|
|
1801
|
+
{
|
|
1802
|
+
workingDir: path3.join(root, subdir),
|
|
1803
|
+
packageRoot: root
|
|
1804
|
+
}
|
|
1805
|
+
);
|
|
2155
1806
|
return {
|
|
2156
1807
|
path: requirementsTxtRelPath,
|
|
2157
1808
|
data: pyproject,
|
|
@@ -2249,7 +1900,8 @@ function parseUvLock(content, path4) {
|
|
|
2249
1900
|
const packages = (parsed.package ?? []).filter((pkg) => pkg.name && pkg.version).map((pkg) => ({
|
|
2250
1901
|
name: pkg.name,
|
|
2251
1902
|
version: pkg.version,
|
|
2252
|
-
source: pkg.source
|
|
1903
|
+
source: pkg.source,
|
|
1904
|
+
wheels: (pkg.wheels ?? []).map((w) => ({ url: w.url }))
|
|
2253
1905
|
}));
|
|
2254
1906
|
return { version: parsed.version, packages };
|
|
2255
1907
|
}
|
|
@@ -2305,6 +1957,20 @@ function classifyPackages(options) {
|
|
|
2305
1957
|
return { privatePackages, publicPackages, packageVersions };
|
|
2306
1958
|
}
|
|
2307
1959
|
|
|
1960
|
+
// src/manifest/wheel-compat.ts
|
|
1961
|
+
async function isWheelCompatible(wheelFilename, pythonMajor, pythonMinor, osName, archName, osMajor, osMinor) {
|
|
1962
|
+
const mod = await importWasmModule();
|
|
1963
|
+
return mod.isWheelCompatible(
|
|
1964
|
+
wheelFilename,
|
|
1965
|
+
pythonMajor,
|
|
1966
|
+
pythonMinor,
|
|
1967
|
+
osName,
|
|
1968
|
+
archName,
|
|
1969
|
+
osMajor,
|
|
1970
|
+
osMinor
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
|
|
2308
1974
|
// src/manifest/python-selector.ts
|
|
2309
1975
|
function selectPython(constraints, available) {
|
|
2310
1976
|
const warnings = [];
|
|
@@ -2617,6 +2283,7 @@ export {
|
|
|
2617
2283
|
extendDistRecord,
|
|
2618
2284
|
getStringConstant,
|
|
2619
2285
|
isPrivatePackageSource,
|
|
2286
|
+
isWheelCompatible,
|
|
2620
2287
|
normalizePackageName2 as normalizePackageName,
|
|
2621
2288
|
parseUvLock,
|
|
2622
2289
|
scanDistributions,
|