diff options
| -rw-r--r-- | src/api/schema.d.ts | 98 | ||||
| -rw-r--r-- | src/api/schema.js | 421 | ||||
| -rw-r--r-- | src/api/schema.peechy | 62 | ||||
| -rw-r--r-- | src/api/schema.zig | 326 | ||||
| -rw-r--r-- | src/cli/install_command.zig | 42 | ||||
| -rw-r--r-- | src/install/install.zig | 29 | ||||
| -rw-r--r-- | src/install/semver.zig | 394 |
7 files changed, 1372 insertions, 0 deletions
diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts index d00dcfa6e..36be0a593 100644 --- a/src/api/schema.d.ts +++ b/src/api/schema.d.ts @@ -256,6 +256,22 @@ export const WebsocketCommandKindKeys = { 2: "manifest", manifest: "manifest", }; +export enum NPMPackageDataKind { + tarball = 1, + http = 2, + symlink = 3, + workspace = 4, +} +export const NPMPackageDataKindKeys = { + 1: "tarball", + tarball: "tarball", + 2: "http", + http: "http", + 3: "symlink", + symlink: "symlink", + 4: "workspace", + workspace: "workspace", +}; export interface StackFrame { function_name: string; file: string; @@ -608,6 +624,58 @@ export interface WebsocketMessageManifestFailure { log: Log; } +export interface SemverQualifier { + pre?: string; + build?: string; +} + +export interface Semver { + major: uint32; + minor: uint32; + patch: uint32; + raw: StringPointer; + qualifiers: SemverQualifier[]; +} + +export interface NPMPackageData { + kind: NPMPackageDataKind; + value: StringPointer; + integrity: StringPointer; + destination: StringPointer; +} + +export interface NPMPackage { + id: uint32; + name: StringPointer; + version: StringPointer; + resolution: Semver; + data: NPMPackageData; + dependencies_hash: uint32; + dev_dependencies_hash: uint32; + peer_dependencies_hash: uint32; + optional_dependencies_hash: uint32; + dependencies: DependencyResolution[]; +} + +export interface DependencyResolution { + version: Semver; + package: uint32; + required: boolean; + optional: boolean; + peer: boolean; + dev: boolean; +} + +export interface Lockfile { + version?: string; + registry?: string; + root?: uint32; + hashes?: Uint32Array; + name_hashes?: Uint32Array; + packages?: NPMPackage[]; + blob?: Uint8Array; +} + export declare function encodeStackFrame( message: StackFrame, bb: ByteBuffer @@ -897,3 +965,33 @@ export declare function encodeWebsocketMessageManifestFailure( export declare function decodeWebsocketMessageManifestFailure( buffer: ByteBuffer ): WebsocketMessageManifestFailure; +export declare function encodeSemverQualifier( + message: SemverQualifier, + bb: ByteBuffer +): void; +export declare function decodeSemverQualifier( + buffer: ByteBuffer +): SemverQualifier; +export declare function encodeSemver(message: Semver, bb: ByteBuffer): void; +export declare function decodeSemver(buffer: ByteBuffer): Semver; +export declare function encodeNPMPackageData( + message: NPMPackageData, + bb: ByteBuffer +): void; +export declare function decodeNPMPackageData( + buffer: ByteBuffer +): NPMPackageData; +export declare function encodeNPMPackage( + message: NPMPackage, + bb: ByteBuffer +): void; +export declare function decodeNPMPackage(buffer: ByteBuffer): NPMPackage; +export declare function encodeDependencyResolution( + message: DependencyResolution, + bb: ByteBuffer +): void; +export declare function decodeDependencyResolution( + buffer: ByteBuffer +): DependencyResolution; +export declare function encodeLockfile(message: Lockfile, bb: ByteBuffer): void; +export declare function decodeLockfile(buffer: ByteBuffer): Lockfile; diff --git a/src/api/schema.js b/src/api/schema.js index dc89e2b11..80ab949aa 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -2863,6 +2863,413 @@ function encodeWebsocketMessageManifestFailure(message, bb) { } } +function decodeSemverQualifier(bb) { + var result = {}; + + while (true) { + switch (bb.readByte()) { + case 0: + return result; + + case 1: + result["pre"] = bb.readString(); + break; + + case 2: + result["build"] = bb.readString(); + break; + + default: + throw new Error("Attempted to parse invalid message"); + } + } +} + +function encodeSemverQualifier(message, bb) { + var value = message["pre"]; + if (value != null) { + bb.writeByte(1); + bb.writeString(value); + } + + var value = message["build"]; + if (value != null) { + bb.writeByte(2); + bb.writeString(value); + } + bb.writeByte(0); +} + +function decodeSemver(bb) { + var result = {}; + + result["major"] = bb.readUint32(); + result["minor"] = bb.readUint32(); + result["patch"] = bb.readUint32(); + result["raw"] = decodeStringPointer(bb); + var length = bb.readVarUint(); + var values = (result["qualifiers"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = decodeSemverQualifier(bb); + return result; +} + +function encodeSemver(message, bb) { + var value = message["major"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "major"'); + } + + var value = message["minor"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "minor"'); + } + + var value = message["patch"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "patch"'); + } + + var value = message["raw"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "raw"'); + } + + var value = message["qualifiers"]; + if (value != null) { + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + encodeSemverQualifier(value, bb); + } + } else { + throw new Error('Missing required field "qualifiers"'); + } +} +const NPMPackageDataKind = { + 1: 1, + 2: 2, + 3: 3, + 4: 4, + tarball: 1, + http: 2, + symlink: 3, + workspace: 4, +}; +const NPMPackageDataKindKeys = { + 1: "tarball", + 2: "http", + 3: "symlink", + 4: "workspace", + tarball: "tarball", + http: "http", + symlink: "symlink", + workspace: "workspace", +}; + +function decodeNPMPackageData(bb) { + var result = {}; + + result["kind"] = NPMPackageDataKind[bb.readVarUint()]; + result["value"] = decodeStringPointer(bb); + result["integrity"] = decodeStringPointer(bb); + result["destination"] = decodeStringPointer(bb); + return result; +} + +function encodeNPMPackageData(message, bb) { + var value = message["kind"]; + if (value != null) { + var encoded = NPMPackageDataKind[value]; + if (encoded === void 0) + throw new Error( + "Invalid value " + + JSON.stringify(value) + + ' for enum "NPMPackageDataKind"' + ); + bb.writeVarUint(encoded); + } else { + throw new Error('Missing required field "kind"'); + } + + var value = message["value"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "value"'); + } + + var value = message["integrity"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "integrity"'); + } + + var value = message["destination"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "destination"'); + } +} + +function decodeNPMPackage(bb) { + var result = {}; + + result["id"] = bb.readUint32(); + result["name"] = decodeStringPointer(bb); + result["version"] = decodeStringPointer(bb); + result["resolution"] = decodeSemver(bb); + result["data"] = decodeNPMPackageData(bb); + result["dependencies_hash"] = bb.readUint32(); + result["dev_dependencies_hash"] = bb.readUint32(); + result["peer_dependencies_hash"] = bb.readUint32(); + result["optional_dependencies_hash"] = bb.readUint32(); + var length = bb.readVarUint(); + var values = (result["dependencies"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = decodeDependencyResolution(bb); + return result; +} + +function encodeNPMPackage(message, bb) { + var value = message["id"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "id"'); + } + + var value = message["name"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "name"'); + } + + var value = message["version"]; + if (value != null) { + encodeStringPointer(value, bb); + } else { + throw new Error('Missing required field "version"'); + } + + var value = message["resolution"]; + if (value != null) { + encodeSemver(value, bb); + } else { + throw new Error('Missing required field "resolution"'); + } + + var value = message["data"]; + if (value != null) { + encodeNPMPackageData(value, bb); + } else { + throw new Error('Missing required field "data"'); + } + + var value = message["dependencies_hash"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "dependencies_hash"'); + } + + var value = message["dev_dependencies_hash"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "dev_dependencies_hash"'); + } + + var value = message["peer_dependencies_hash"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "peer_dependencies_hash"'); + } + + var value = message["optional_dependencies_hash"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "optional_dependencies_hash"'); + } + + var value = message["dependencies"]; + if (value != null) { + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + encodeDependencyResolution(value, bb); + } + } else { + throw new Error('Missing required field "dependencies"'); + } +} + +function decodeDependencyResolution(bb) { + var result = {}; + + result["version"] = decodeSemver(bb); + result["package"] = bb.readUint32(); + result["required"] = !!bb.readByte(); + result["optional"] = !!bb.readByte(); + result["peer"] = !!bb.readByte(); + result["dev"] = !!bb.readByte(); + return result; +} + +function encodeDependencyResolution(message, bb) { + var value = message["version"]; + if (value != null) { + encodeSemver(value, bb); + } else { + throw new Error('Missing required field "version"'); + } + + var value = message["package"]; + if (value != null) { + bb.writeUint32(value); + } else { + throw new Error('Missing required field "package"'); + } + + var value = message["required"]; + if (value != null) { + bb.writeByte(value); + } else { + throw new Error('Missing required field "required"'); + } + + var value = message["optional"]; + if (value != null) { + bb.writeByte(value); + } else { + throw new Error('Missing required field "optional"'); + } + + var value = message["peer"]; + if (value != null) { + bb.writeByte(value); + } else { + throw new Error('Missing required field "peer"'); + } + + var value = message["dev"]; + if (value != null) { + bb.writeByte(value); + } else { + throw new Error('Missing required field "dev"'); + } +} + +function decodeLockfile(bb) { + var result = {}; + + while (true) { + switch (bb.readByte()) { + case 0: + return result; + + case 1: + result["version"] = bb.readString(); + break; + + case 2: + result["registry"] = bb.readString(); + break; + + case 3: + result["root"] = bb.readUint32(); + break; + + case 4: + result["hashes"] = bb.readUint32ByteArray(); + break; + + case 5: + result["name_hashes"] = bb.readUint32ByteArray(); + break; + + case 6: + var length = bb.readVarUint(); + var values = (result["packages"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = decodeNPMPackage(bb); + break; + + case 7: + result["blob"] = bb.readByteArray(); + break; + + default: + throw new Error("Attempted to parse invalid message"); + } + } +} + +function encodeLockfile(message, bb) { + var value = message["version"]; + if (value != null) { + bb.writeByte(1); + bb.writeString(value); + } + + var value = message["registry"]; + if (value != null) { + bb.writeByte(2); + bb.writeString(value); + } + + var value = message["root"]; + if (value != null) { + bb.writeByte(3); + bb.writeUint32(value); + } + + var value = message["hashes"]; + if (value != null) { + bb.writeByte(4); + bb.writeUint32ByteArray(value); + } + + var value = message["name_hashes"]; + if (value != null) { + bb.writeByte(5); + bb.writeUint32ByteArray(value); + } + + var value = message["packages"]; + if (value != null) { + bb.writeByte(6); + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + encodeNPMPackage(value, bb); + } + } + + var value = message["blob"]; + if (value != null) { + bb.writeByte(7); + bb.writeByteArray(value); + } + bb.writeByte(0); +} + export { Loader }; export { LoaderKeys }; export { FrameworkEntryPointType }; @@ -2997,3 +3404,17 @@ export { decodeWebsocketMessageManifestSuccess }; export { encodeWebsocketMessageManifestSuccess }; export { decodeWebsocketMessageManifestFailure }; export { encodeWebsocketMessageManifestFailure }; +export { decodeSemverQualifier }; +export { encodeSemverQualifier }; +export { decodeSemver }; +export { encodeSemver }; +export { NPMPackageDataKind }; +export { NPMPackageDataKindKeys }; +export { decodeNPMPackageData }; +export { encodeNPMPackageData }; +export { decodeNPMPackage }; +export { encodeNPMPackage }; +export { decodeDependencyResolution }; +export { encodeDependencyResolution }; +export { decodeLockfile }; +export { encodeLockfile }; diff --git a/src/api/schema.peechy b/src/api/schema.peechy index 2cc78504a..035a7fa26 100644 --- a/src/api/schema.peechy +++ b/src/api/schema.peechy @@ -537,4 +537,66 @@ struct WebsocketMessageManifestFailure { Log log; } +message SemverQualifier { + string pre = 1; + string build = 2; +} + +struct Semver { + uint32 major; + uint32 minor; + uint32 patch; + StringPointer raw; + SemverQualifier[] qualifiers; +} + +enum NPMPackageDataKind { + tarball = 1; + http = 2; + symlink = 3; + workspace = 4; +} + +struct NPMPackageData { + NPMPackageDataKind kind; + StringPointer value; + StringPointer integrity; + StringPointer destination; +} + +struct NPMPackage { + uint32 id; + + StringPointer name; + StringPointer version; + Semver resolution; + NPMPackageData data; + + uint32 dependencies_hash; + uint32 dev_dependencies_hash; + uint32 peer_dependencies_hash; + uint32 optional_dependencies_hash; + + DependencyResolution[] dependencies; +} + +struct DependencyResolution { + Semver version; + uint32 package; + + bool required; + bool optional; + bool peer; + bool dev; +} + +message Lockfile { + string version = 1; + string registry = 2; + uint32 root = 3; + uint32[] hashes = 4; + uint32[] name_hashes = 5; + NPMPackage[] packages = 6; + byte[] blob = 7; +} diff --git a/src/api/schema.zig b/src/api/schema.zig index 45589a431..3c27a7fad 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -2693,4 +2693,330 @@ pub const Api = struct { try writer.writeValue(@TypeOf(this.log), this.log); } }; + + pub const SemverQualifier = struct { + /// pre + pre: ?[]const u8 = null, + + /// build + build: ?[]const u8 = null, + + pub fn decode(reader: anytype) anyerror!SemverQualifier { + var this = std.mem.zeroes(SemverQualifier); + + while (true) { + switch (try reader.readByte()) { + 0 => { + return this; + }, + + 1 => { + this.pre = try reader.readValue([]const u8); + }, + 2 => { + this.build = try reader.readValue([]const u8); + }, + else => { + return error.InvalidMessage; + }, + } + } + unreachable; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + if (this.pre) |pre| { + try writer.writeFieldID(1); + try writer.writeValue(@TypeOf(pre), pre); + } + if (this.build) |build| { + try writer.writeFieldID(2); + try writer.writeValue(@TypeOf(build), build); + } + try writer.endMessage(); + } + }; + + pub const Semver = struct { + /// major + major: u32 = 0, + + /// minor + minor: u32 = 0, + + /// patch + patch: u32 = 0, + + /// raw + raw: StringPointer, + + /// qualifiers + qualifiers: []const SemverQualifier, + + pub fn decode(reader: anytype) anyerror!Semver { + var this = std.mem.zeroes(Semver); + + this.major = try reader.readValue(u32); + this.minor = try reader.readValue(u32); + this.patch = try reader.readValue(u32); + this.raw = try reader.readValue(StringPointer); + this.qualifiers = try reader.readArray(SemverQualifier); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.major); + try writer.writeInt(this.minor); + try writer.writeInt(this.patch); + try writer.writeValue(@TypeOf(this.raw), this.raw); + try writer.writeArray(SemverQualifier, this.qualifiers); + } + }; + + pub const NpmPackageDataKind = enum(u32) { + _none, + /// tarball + tarball, + + /// http + http, + + /// symlink + symlink, + + /// workspace + workspace, + + _, + + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } + }; + + pub const NpmPackageData = struct { + /// kind + kind: NpmPackageDataKind, + + /// value + value: StringPointer, + + /// integrity + integrity: StringPointer, + + /// destination + destination: StringPointer, + + pub fn decode(reader: anytype) anyerror!NpmPackageData { + var this = std.mem.zeroes(NpmPackageData); + + this.kind = try reader.readValue(NpmPackageDataKind); + this.value = try reader.readValue(StringPointer); + this.integrity = try reader.readValue(StringPointer); + this.destination = try reader.readValue(StringPointer); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.kind); + try writer.writeValue(@TypeOf(this.value), this.value); + try writer.writeValue(@TypeOf(this.integrity), this.integrity); + try writer.writeValue(@TypeOf(this.destination), this.destination); + } + }; + + pub const NpmPackage = struct { + /// id + id: u32 = 0, + + /// name + name: StringPointer, + + /// version + version: StringPointer, + + /// resolution + resolution: Semver, + + /// data + data: NpmPackageData, + + /// dependencies_hash + dependencies_hash: u32 = 0, + + /// dev_dependencies_hash + dev_dependencies_hash: u32 = 0, + + /// peer_dependencies_hash + peer_dependencies_hash: u32 = 0, + + /// optional_dependencies_hash + optional_dependencies_hash: u32 = 0, + + /// dependencies + dependencies: []const DependencyResolution, + + pub fn decode(reader: anytype) anyerror!NpmPackage { + var this = std.mem.zeroes(NpmPackage); + + this.id = try reader.readValue(u32); + this.name = try reader.readValue(StringPointer); + this.version = try reader.readValue(StringPointer); + this.resolution = try reader.readValue(Semver); + this.data = try reader.readValue(NpmPackageData); + this.dependencies_hash = try reader.readValue(u32); + this.dev_dependencies_hash = try reader.readValue(u32); + this.peer_dependencies_hash = try reader.readValue(u32); + this.optional_dependencies_hash = try reader.readValue(u32); + this.dependencies = try reader.readArray(DependencyResolution); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeValue(@TypeOf(this.name), this.name); + try writer.writeValue(@TypeOf(this.version), this.version); + try writer.writeValue(@TypeOf(this.resolution), this.resolution); + try writer.writeValue(@TypeOf(this.data), this.data); + try writer.writeInt(this.dependencies_hash); + try writer.writeInt(this.dev_dependencies_hash); + try writer.writeInt(this.peer_dependencies_hash); + try writer.writeInt(this.optional_dependencies_hash); + try writer.writeArray(DependencyResolution, this.dependencies); + } + }; + + pub const DependencyResolution = struct { + /// version + version: Semver, + + /// package + package: u32 = 0, + + /// required + required: bool = false, + + /// optional + optional: bool = false, + + /// peer + peer: bool = false, + + /// dev + dev: bool = false, + + pub fn decode(reader: anytype) anyerror!DependencyResolution { + var this = std.mem.zeroes(DependencyResolution); + + this.version = try reader.readValue(Semver); + this.package = try reader.readValue(u32); + this.required = try reader.readValue(bool); + this.optional = try reader.readValue(bool); + this.peer = try reader.readValue(bool); + this.dev = try reader.readValue(bool); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(@TypeOf(this.version), this.version); + try writer.writeInt(this.package); + try writer.writeInt(@as(u8, @boolToInt(this.required))); + try writer.writeInt(@as(u8, @boolToInt(this.optional))); + try writer.writeInt(@as(u8, @boolToInt(this.peer))); + try writer.writeInt(@as(u8, @boolToInt(this.dev))); + } + }; + + pub const Lockfile = struct { + /// version + version: ?[]const u8 = null, + + /// registry + registry: ?[]const u8 = null, + + /// root + root: ?u32 = null, + + /// hashes + hashes: []const u32, + + /// name_hashes + name_hashes: []const u32, + + /// packages + packages: []const NpmPackage, + + /// blob + blob: []const u8, + + pub fn decode(reader: anytype) anyerror!Lockfile { + var this = std.mem.zeroes(Lockfile); + + while (true) { + switch (try reader.readByte()) { + 0 => { + return this; + }, + + 1 => { + this.version = try reader.readValue([]const u8); + }, + 2 => { + this.registry = try reader.readValue([]const u8); + }, + 3 => { + this.root = try reader.readValue(u32); + }, + 4 => { + this.hashes = try reader.readArray(u32); + }, + 5 => { + this.name_hashes = try reader.readArray(u32); + }, + 6 => { + this.packages = try reader.readArray(NpmPackage); + }, + 7 => { + this.blob = try reader.readArray(u8); + }, + else => { + return error.InvalidMessage; + }, + } + } + unreachable; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + if (this.version) |version| { + try writer.writeFieldID(1); + try writer.writeValue(@TypeOf(version), version); + } + if (this.registry) |registry| { + try writer.writeFieldID(2); + try writer.writeValue(@TypeOf(registry), registry); + } + if (this.root) |root| { + try writer.writeFieldID(3); + try writer.writeInt(root); + } + if (this.hashes) |hashes| { + try writer.writeFieldID(4); + try writer.writeArray(u32, hashes); + } + if (this.name_hashes) |name_hashes| { + try writer.writeFieldID(5); + try writer.writeArray(u32, name_hashes); + } + if (this.packages) |packages| { + try writer.writeFieldID(6); + try writer.writeArray(NpmPackage, packages); + } + if (this.blob) |blob| { + try writer.writeFieldID(7); + try writer.writeArray(u8, blob); + } + try writer.endMessage(); + } + }; }; diff --git a/src/cli/install_command.zig b/src/cli/install_command.zig new file mode 100644 index 000000000..d4af860b8 --- /dev/null +++ b/src/cli/install_command.zig @@ -0,0 +1,42 @@ +usingnamespace @import("../global.zig"); +const std = @import("std"); + +const lex = @import("../js_lexer.zig"); +const logger = @import("../logger.zig"); +const alloc = @import("../alloc.zig"); +const options = @import("../options.zig"); +const js_parser = @import("../js_parser.zig"); +const json_parser = @import("../json_parser.zig"); +const js_printer = @import("../js_printer.zig"); +const js_ast = @import("../js_ast.zig"); +const linker = @import("../linker.zig"); +usingnamespace @import("../ast/base.zig"); +usingnamespace @import("../defines.zig"); +const panicky = @import("../panic_handler.zig"); +const sync = @import("../sync.zig"); +const Api = @import("../api/schema.zig").Api; +const resolve_path = @import("../resolver/resolve_path.zig"); +const configureTransformOptionsForBun = @import("../javascript/jsc/config.zig").configureTransformOptionsForBun; +const Command = @import("../cli.zig").Command; +const bundler = @import("../bundler.zig"); +const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; +const DotEnv = @import("../env_loader.zig"); +const which = @import("../which.zig").which; +const Run = @import("../bun_js.zig").Run; +var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; +var path_buf2: [std.fs.MAX_PATH_BYTES]u8 = undefined; +const NpmArgs = struct { + // https://github.com/npm/rfcs/blob/main/implemented/0021-reduce-lifecycle-script-environment.md#detailed-explanation + pub const package_name: string = "npm_package_name"; + pub const package_version: string = "npm_package_version"; +}; + +const yarn_commands: []u64 = @import("./list-of-yarn-commands.zig").all_yarn_commands; + +const ShellCompletions = @import("./shell_completions.zig"); + +pub const InstallCommand = struct { + pub fn exec(ctx: Command.Context) !void { + + } +}; diff --git a/src/install/install.zig b/src/install/install.zig new file mode 100644 index 000000000..7c4324807 --- /dev/null +++ b/src/install/install.zig @@ -0,0 +1,29 @@ +usingnamespace @import("../global.zig"); +const std = @import("std"); + +const lex = @import("../js_lexer.zig"); +const logger = @import("../logger.zig"); +const alloc = @import("../alloc.zig"); +const options = @import("../options.zig"); +const js_parser = @import("../js_parser.zig"); +const json_parser = @import("../json_parser.zig"); +const js_printer = @import("../js_printer.zig"); +const js_ast = @import("../js_ast.zig"); +const linker = @import("../linker.zig"); +usingnamespace @import("../ast/base.zig"); +usingnamespace @import("../defines.zig"); +const panicky = @import("../panic_handler.zig"); +const sync = @import("../sync.zig"); +const Api = @import("../api/schema.zig").Api; +const resolve_path = @import("../resolver/resolve_path.zig"); +const configureTransformOptionsForBun = @import("../javascript/jsc/config.zig").configureTransformOptionsForBun; +const Command = @import("../cli.zig").Command; +const bundler = @import("../bundler.zig"); +const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; +const DotEnv = @import("../env_loader.zig"); +const which = @import("../which.zig").which; +const Run = @import("../bun_js.zig").Run; + +var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; +var path_buf2: [std.fs.MAX_PATH_BYTES]u8 = undefined; + diff --git a/src/install/semver.zig b/src/install/semver.zig new file mode 100644 index 000000000..14eb3097a --- /dev/null +++ b/src/install/semver.zig @@ -0,0 +1,394 @@ +usingnamespace @import("../global.zig"); +const std = @import("std"); + +pub const Version = struct { + major: u32 = 0, + minor: u32 = 0, + patch: u32 = 0, + tag: Tag = Tag{}, + extra_tags: []const Tag = &[_]Tag{}, + raw: strings.StringOrTinyString = strings.StringOrTinyString{}, + + inline fn atPart(i: u8) u32 { + return switch (i) { + 0 => self.major, + 1 => self.minor, + 2 => self.patch, + else => unreachable, + }; + } + + pub const Tag = struct { + pre: strings.StringOrTinyString = strings.StringOrTinyString{}, + build: strings.StringOrTinyString = strings.StringOrTinyString{}, + + pub const TagResult = struct { + tag: Tag = Tag{}, + extra_tags: []const Tag = &[_]Tag{}, + len: u32 = 0, + }; + pub fn parse(allocator: *std.mem.Allocator, input: string) TagResult {} + }; + + pub fn isGreaterThan(self: Version, other: Version) bool { + if (self.major > other.major) { + return true; + } + + if (self.minor > other.minor) { + return true; + } + + if (self.patch > other.patch) { + return true; + } + } + + pub const ParseResult = struct { + wildcard: Query.Token.Wildcard = Query.Token.Wildcard.none, + valid: bool = true, + version: Version = Version{}, + stopped_at: u32 = 0, + }; + + pub fn parse(input: string, allocator: *std.mem.Allocator) ParseResult { + var result = ParseResult{}; + + var part_i_: i8 = -1; + var part_start_i_: i32 = -1; + var last_char_i: u32 = 0; + + if (input.len == 0) { + result.valid = false; + return result; + } + var is_done = false; + var stopped_at: i32 = 0; + for (input) |char, i| { + if (is_done) { + break; + } + + stopped_at = i; + switch (char) { + ' ' => { + if (part_i_ > 2) { + is_done = true; + break; + } + }, + '|', '^', '#', '&', '%', '!' => { + is_done = true; + stopped_at -= 1; + break; + }, + '0'...'9' => { + if (part_start_i_ == -1) { + part_start_i_ = @intCast(i32, i); + } + last_char_i = @intCast(u32, i); + }, + '.' => { + if (part_start_i_ > -1 and part_i <= 2) { + switch (part_i) { + 0 => { + result.version.major = parseVersionNumber(input[@intCast(usize, part_start_i)..i]); + }, + 1 => { + result.version.minor = parseVersionNumber(input[@intCast(usize, part_start_i)..i]); + }, + else => {}, + } + + part_start_i_ = -1; + part_i_ += 1; + // "fo.o.b.ar" + } else if (part_i > 2 or part_start_i_ == -1) { + result.valid = false; + is_done = true; + break; + } + }, + '-', '+' => { + if (part_i == 2 and part_start_i_ > -1) { + result.version.patch = parseVersionNumber(input[@intCast(usize, part_start_i)..i]); + result.wildcard = Query.Token.Wildcard.none; + part_start_i_ = @intCast(i32, i); + part_i_ = 3; + is_done = true; + break; + } else { + result.valid = false; + is_done = true; + break; + } + }, + 'x', '*', 'X' => { + if (part_start_i_ == -1) { + part_start_i_ = @intCast(i32, i); + } + last_char_i = @intCast(u32, i); + + // We want min wildcard + if (result.wildcard == .none) { + switch (part_i_) { + 0 => { + result.wildcard = Query.Token.Wildcard.major; + }, + 1 => { + result.wildcard = Query.Token.Wildcard.minor; + }, + 2 => { + result.wildcard = Query.Token.Wildcard.patch; + }, + else => unreachable, + } + } + }, + else => { + last_char_i = 0; + result.is_valid = false; + is_done = true; + break; + }, + } + } + + const part_i = @intCast(u8, @maximum(0, part_i_)); + result.valid = result.valid and part_i_ > -1; + + const part_start_i = @intCast(u32, @maximum(0, part_start_i_)); + + if (last_char_i == -1 or part_start_i > last_char_i) + last_char_i = input.len - 1; + + // Where did we leave off? + switch (part_i) { + // That means they used a match like this: + // "1" + // So its a wildcard major + 0 => { + if (result.wildcard == .none) { + result.wildcard = Query.Token.Wildcard.minor; + } + + result.version.major = parseVersionNumber(input[@as(usize, part_start_i) .. last_char_i + 1]); + }, + 1 => { + if (result.wildcard == .none) { + result.wildcard = Query.Token.Wildcard.patch; + } + + result.version.minor = parseVersionNumber(input[@as(usize, part_start_i) .. last_char_i + 1]); + }, + 2 => { + result.version.patch = parseVersionNumber(input[@as(usize, part_start_i) .. last_char_i + 1]); + }, + 3 => { + const tag_result = Tag.parse(allocator, input[part_start_i..]); + result.version.tag = tag_result.tag; + if (tag_result.extra_tags.len > 0) { + result.version.extra_tags = tag_result.extra_tags; + } + + stopped_at = @intCast(i32, tag_result.len) + part_start_i; + }, + else => {}, + } + + result.stopped_at = @intCast(u32, @maximum(stopped_at, 0)); + + return result; + } + + fn parseVersionNumber(input: string) u32 { + // max decimal u32 is 4294967295 + var bytes: [10]u8 = undefined; + var byte_i: u8 = 0; + + for (input) |char, i| { + switch (char) { + 'X', 'x', '*' => return 0, + '0'...'9' => { + // out of bounds + if (byte_i + 1 > bytes.len) return 0; + bytes[byte_i] = char; + byte_i += 1; + }, + // ignore invalid characters + else => {}, + } + } + + // If there are no numbers, it's 0. + if (byte_i == 0) return 0; + + return std.fmt.parseInt(u32, bytes[0..byte_i], 10) catch 0; + } +}; + +pub const Range = struct { + pub const Op = enum { + eql, + lt, + lte, + gt, + gte, + }; + + pub const Comparator = struct { + op: Op = Op.eql, + version: Version = Version{}, + }; + + left: Comparator = Comparator{}, + right: ?Comparator = null, +}; + +pub const Query = struct { + pub const Op = enum { + none, + AND, + OR, + }; + + range: Range = Range{}, + next_op: Op = Op.none, + + next: *Query = undefined, + + pub const List = struct { + head: Query, + tail: ?*Query = null, + allocator: *std.mem.Allocator, + pub fn setVersion(self: *List, version: Version) void {} + + pub fn andRange(self: *List, range: Range) !void {} + + pub fn orRange(self: *List, range: Range) !void {} + }; + + pub const Token = struct { + tag: Tag = Tag.none, + wildcard: Wildcard = Wildcard.none, + + pub const Tag = enum { + none, + logical_or, + gt, + gte, + lt, + lte, + version, + tilda, + caret, + }; + + pub const Wildcard = enum { + none, + major, + minor, + patch, + }; + }; + + pub fn parse(allocator: *std.mem.Allocator, input: string) !Query { + var i: usize = 0; + var query = Query{}; + + var token = Token{}; + var prev_token = Token{}; + + var count: u8 = 0; + var skip_round = false; + var is_or = false; + + while (i < input.len) { + skip_round = false; + + switch (input[i]) { + '>' => { + if (prev_token.tag == .version) { + is_or = false; + } + + if (input.len > i + 1 and input[i + 1] == '=') { + token.tag = .gte; + i += 1; + } else { + token.tag = .gt; + } + + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + '<' => { + if (input.len > i + 1 and input[i + 1] == '=') { + token.tag = .lte; + i += 1; + } else { + token.tag = .lt; + } + + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + '=', 'v' => { + token.tag = .version; + is_or = true; + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + '~' => { + token.tag = .tilda; + i += 1; + + if (i < input.len and input[i] == '>') i += 1; + + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + '^' => { + token.tag = .caret; + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + '0'...'9', 'X', 'x', '*' => { + token.tag = .version; + is_or = true; + }, + '|' => { + i += 1; + + while (i < input.len and input[i] == '|') : (i += 1) {} + while (i < input.len and input[i] == ' ') : (i += 1) {} + is_or = true; + token.tag = Token.Tag.none; + skip_round = true; + }, + '-' => { + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + }, + ' ' => { + i += 1; + while (i < input.len and input[i] == ' ') : (i += 1) {} + skip_round = true; + }, + else => { + i += 1; + token.tag = Token.Tag.none; + skip_round = true; + }, + } + + if (!skip_round) { + if (count == 0 and token.tag == .version) { + prev_token.tag = token.tag; + const parse_result = Version.parse(input[i..]); + } + } + } + + return query; + } +}; |
