diff options
Diffstat (limited to 'src/install')
-rw-r--r-- | src/install/dependency.zig | 62 | ||||
-rw-r--r-- | src/install/install.zig | 57 | ||||
-rw-r--r-- | src/install/lockfile.zig | 5 | ||||
-rw-r--r-- | src/install/migration.zig | 1 |
4 files changed, 98 insertions, 27 deletions
diff --git a/src/install/dependency.zig b/src/install/dependency.zig index ca0d702aa..813ec9ac5 100644 --- a/src/install/dependency.zig +++ b/src/install/dependency.zig @@ -2,6 +2,7 @@ const bun = @import("root").bun; const logger = bun.logger; const Environment = @import("../env.zig"); const Install = @import("./install.zig"); +const PackageManager = Install.PackageManager; const ExternalStringList = Install.ExternalStringList; const Features = Install.Features; const PackageNameHash = Install.PackageNameHash; @@ -92,6 +93,7 @@ pub fn cloneWithDifferentBuffers(this: *const Dependency, name_buf: []const u8, .version = Dependency.parseWithTag( builder.lockfile.allocator, new_name, + String.Builder.stringHash(new_name.slice(out_slice)), new_literal.slice(out_slice), this.version.tag, &sliced, @@ -144,11 +146,12 @@ pub fn toDependency( const name = String{ .bytes = this[0..8].*, }; + const name_hash: u64 = @bitCast(this[8..16].*); return Dependency{ .name = name, - .name_hash = @as(u64, @bitCast(this[8..16].*)), + .name_hash = name_hash, .behavior = @bitCast(this[16]), - .version = Dependency.Version.toVersion(name, this[17..this.len].*, ctx), + .version = Dependency.Version.toVersion(name, name_hash, this[17..this.len].*, ctx), }; } @@ -297,6 +300,7 @@ pub const Version = struct { pub fn toVersion( alias: String, + alias_hash: PackageNameHash, bytes: Version.External, ctx: Dependency.Context, ) Dependency.Version { @@ -306,6 +310,7 @@ pub const Version = struct { return Dependency.parseWithTag( ctx.allocator, alias, + alias_hash, sliced.slice, tag, sliced, @@ -617,6 +622,7 @@ pub const Version = struct { pub const NpmInfo = struct { name: String, version: Semver.Query.Group, + is_alias: bool = false, fn eql(this: NpmInfo, that: NpmInfo, this_buf: []const u8, that_buf: []const u8) bool { return this.name.eql(that.name, this_buf, that_buf) and this.version.eql(that.version); @@ -670,17 +676,19 @@ pub fn eql( pub inline fn parse( allocator: std.mem.Allocator, alias: String, + alias_hash: ?PackageNameHash, dependency: string, sliced: *const SlicedString, log: ?*logger.Log, ) ?Version { const dep = std.mem.trimLeft(u8, dependency, " \t\n\r"); - return parseWithTag(allocator, alias, dep, Version.Tag.infer(dep), sliced, log); + return parseWithTag(allocator, alias, alias_hash, dep, Version.Tag.infer(dep), sliced, log); } pub fn parseWithOptionalTag( allocator: std.mem.Allocator, alias: String, + alias_hash: ?PackageNameHash, dependency: string, tag: ?Dependency.Version.Tag, sliced: *const SlicedString, @@ -690,6 +698,7 @@ pub fn parseWithOptionalTag( return parseWithTag( allocator, alias, + alias_hash, dep, tag orelse Version.Tag.infer(dep), sliced, @@ -700,6 +709,7 @@ pub fn parseWithOptionalTag( pub fn parseWithTag( allocator: std.mem.Allocator, alias: String, + alias_hash: ?PackageNameHash, dependency: string, tag: Dependency.Version.Tag, sliced: *const SlicedString, @@ -710,19 +720,30 @@ pub fn parseWithTag( switch (tag) { .npm => { var input = dependency; - const name = if (strings.hasPrefixComptime(input, "npm:")) sliced.sub(brk: { - var str = input["npm:".len..]; - var i: usize = @intFromBool(str.len > 0 and str[0] == '@'); - - while (i < str.len) : (i += 1) { - if (str[i] == '@') { - input = str[i + 1 ..]; - break :brk str[0..i]; + + var is_alias = false; + const name = brk: { + if (strings.hasPrefixComptime(input, "npm:")) { + is_alias = true; + var str = input["npm:".len..]; + var i: usize = @intFromBool(str.len > 0 and str[0] == '@'); + + while (i < str.len) : (i += 1) { + if (str[i] == '@') { + input = str[i + 1 ..]; + break :brk sliced.sub(str[0..i]).value(); + } } + + input = str[i..]; + + break :brk sliced.sub(str[0..i]).value(); } - input = str[i..]; - break :brk str[0..i]; - }).value() else alias; + + break :brk alias; + }; + + is_alias = is_alias and alias_hash != null; // Strip single leading v // v1.0.0 -> 1.0.0 @@ -740,16 +761,27 @@ pub fn parseWithTag( return null; }; - return .{ + const result = Version{ .literal = sliced.value(), .value = .{ .npm = .{ + .is_alias = is_alias, .name = name, .version = version, }, }, .tag = .npm, }; + + if (is_alias) { + PackageManager.instance.known_npm_aliases.put( + allocator, + alias_hash.?, + result, + ) catch unreachable; + } + + return result; }, .dist_tag => { var tag_to_use = sliced.value(); diff --git a/src/install/install.zig b/src/install/install.zig index 52025aa04..06eb7296e 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -1628,6 +1628,7 @@ const NetworkChannel = sync.Channel(*NetworkTask, .{ .Static = 8192 }); const ThreadPool = bun.ThreadPool; const PackageManifestMap = std.HashMapUnmanaged(PackageNameHash, Npm.PackageManifest, IdentityContext(PackageNameHash), 80); const RepositoryMap = std.HashMapUnmanaged(u64, bun.FileDescriptor, IdentityContext(u64), 80); +const NpmAliasMap = std.HashMapUnmanaged(PackageNameHash, Dependency.Version, IdentityContext(u64), 80); pub const CacheLevel = struct { use_cache_control_headers: bool, @@ -1754,6 +1755,9 @@ pub const PackageManager = struct { uws_event_loop: *uws.Loop, file_poll_store: JSC.FilePoll.Store, + // name hash from alias package name -> aliased package dependency version info + known_npm_aliases: NpmAliasMap = .{}, + const PreallocatedNetworkTasks = std.BoundedArray(NetworkTask, 1024); const NetworkTaskQueue = std.HashMapUnmanaged(u64, void, IdentityContext(u64), 80); pub var verbose_install = false; @@ -3063,20 +3067,47 @@ pub const PackageManager = struct { .dist_tag, .git, .github, .npm, .tarball, .workspace => String.Builder.stringHash(this.lockfile.str(&name)), else => dependency.name_hash, }; + const version = version: { - if (this.lockfile.overrides.get(name_hash)) |new| { - debug("override: {s} -> {s}", .{ this.lockfile.str(&dependency.version.literal), this.lockfile.str(&new.literal) }); - name = switch (new.tag) { - .dist_tag => new.value.dist_tag.name, - .git => new.value.git.package_name, - .github => new.value.github.package_name, - .npm => new.value.npm.name, - .tarball => new.value.tarball.package_name, - else => name, - }; - name_hash = String.Builder.stringHash(this.lockfile.str(&name)); - break :version new; + if (dependency.version.tag == .npm) { + if (this.known_npm_aliases.get(name_hash)) |aliased| { + const group = dependency.version.value.npm.version; + var curr_list: ?*const Semver.Query.List = &aliased.value.npm.version.head; + while (curr_list) |queries| { + var curr: ?*const Semver.Query = &queries.head; + while (curr) |query| { + if (group.satisfies(query.range.left.version) or group.satisfies(query.range.right.version)) { + name = aliased.value.npm.name; + name_hash = String.Builder.stringHash(this.lockfile.str(&name)); + break :version aliased; + } + curr = query.next; + } + curr_list = queries.next; + } + + // fallthrough. a package that matches the name of an alias but does not match + // the version should be enqueued as a normal npm dependency, overrides allowed + } } + + // allow overriding all dependencies unless the dependency is coming directly from an alias, "npm:<this dep>" + if (dependency.version.tag != .npm or !dependency.version.value.npm.is_alias) { + if (this.lockfile.overrides.get(name_hash)) |new| { + debug("override: {s} -> {s}", .{ this.lockfile.str(&dependency.version.literal), this.lockfile.str(&new.literal) }); + name = switch (new.tag) { + .dist_tag => new.value.dist_tag.name, + .git => new.value.git.package_name, + .github => new.value.github.package_name, + .npm => new.value.npm.name, + .tarball => new.value.tarball.package_name, + else => name, + }; + name_hash = String.Builder.stringHash(this.lockfile.str(&name)); + break :version new; + } + } + break :version dependency.version; }; var loaded_manifest: ?Npm.PackageManifest = null; @@ -6386,6 +6417,7 @@ pub const PackageManager = struct { var version = Dependency.parseWithOptionalTag( allocator, if (alias) |name| String.init(input, name) else placeholder, + if (alias) |name| String.Builder.stringHash(name) else null, value, null, &SlicedString.init(input, value), @@ -6400,6 +6432,7 @@ pub const PackageManager = struct { if (Dependency.parseWithOptionalTag( allocator, placeholder, + null, input, null, &SlicedString.init(input, input), diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 5acf326c6..ddbfaba50 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -666,6 +666,7 @@ fn preprocessUpdateRequests(old: *Lockfile, updates: []PackageManager.UpdateRequ dep.version = Dependency.parse( old.allocator, dep.name, + dep.name_hash, sliced.slice, &sliced, null, @@ -2203,6 +2204,7 @@ pub const OverrideMap = struct { .version = Dependency.parse( lockfile.allocator, name, + name_hash, literalSliced.slice, &literalSliced, log, @@ -2780,6 +2782,7 @@ pub const Package = extern struct { .version = Dependency.parse( allocator, name.value, + name.hash, sliced.slice, &sliced, log, @@ -3067,6 +3070,7 @@ pub const Package = extern struct { var dependency_version = Dependency.parseWithOptionalTag( allocator, external_alias.value, + external_alias.hash, sliced.slice, tag, &sliced, @@ -3136,6 +3140,7 @@ pub const Package = extern struct { if (Dependency.parseWithTag( allocator, external_alias.value, + external_alias.hash, path.slice, .workspace, &path, diff --git a/src/install/migration.zig b/src/install/migration.zig index d74be7265..6e32c0e5e 100644 --- a/src/install/migration.zig +++ b/src/install/migration.zig @@ -636,6 +636,7 @@ pub fn migrateNPMLockfile(this: *Lockfile, allocator: Allocator, log: *logger.Lo const version = Dependency.parse( this.allocator, dep_name, + name_hash, sliced.slice, &sliced, log, |