Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(npm): set install-strategy on npm >= 9 #1179

Merged
merged 1 commit into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions lua/mason-core/installer/managers/npm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ local installer = require "mason-core.installer"
local log = require "mason-core.log"
local path = require "mason-core.path"
local platform = require "mason-core.platform"
local semver = require "mason-core.semver"
local spawn = require "mason-core.spawn"

local M = {}

---@async
---@param predicate fun(npm_version: Semver): boolean
---@return boolean
local function npm_version_satisfies(predicate)
return Result.try(function(try)
local npm_versions = try(spawn.npm { "version", "--json" }).stdout
---@type { npm: string }
local versions = try(Result.pcall(vim.json.decode, npm_versions))
---@type Semver
local npm_version = try(semver.parse(versions.npm))
return predicate(npm_version)
end):get_or_else(false)
end

---@async
function M.init()
log.debug "npm: init"
Expand All @@ -18,7 +34,7 @@ function M.init()
"--scope=mason",
})

-- Use global-style. The reasons for this are:
-- Use shallow install-strategy. The reasons for this are:
-- a) To avoid polluting the executables (aka bin-links) that npm creates.
-- b) The installation is, after all, more similar to a "global" installation. We don't really gain
-- any of the benefits of not using global style (e.g., deduping the dependency tree).
Expand All @@ -27,7 +43,11 @@ function M.init()
-- is a bit unreliable across npm versions (especially <7), so we take extra measures to avoid
-- inadvertently polluting global npm config.
try(Result.pcall(function()
ctx.fs:append_file(".npmrc", "global-style=true")
if npm_version_satisfies(_.gte(semver.new "9.0.0")) then
ctx.fs:append_file(".npmrc", "\ninstall-strategy=shallow")
else
ctx.fs:append_file(".npmrc", "\nglobal-style=true")
end
end))

ctx.stdio_sink.stdout "Initialized npm root\n"
Expand Down
9 changes: 7 additions & 2 deletions lua/mason-core/semver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ local semver = require "mason-vendor.semver"
local M = {}

---@param version string
function M.parse(version)
function M.new(version)
version = version:gsub("^v", "")
return Result.pcall(semver, version)
return semver(version)
end

---@param version string
function M.parse(version)
return Result.pcall(M.new, version)
end

return M
11 changes: 11 additions & 0 deletions lua/mason-vendor/semver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,28 @@ local function smallerPrerelease(mine, other)
return smallerIdList(splitByDot(mine), splitByDot(other))
end

---@class ISemver
local methods = {}

---@return Semver
function methods:nextMajor()
return semver(self.major + 1, 0, 0)
end
---@return Semver
function methods:nextMinor()
return semver(self.major, self.minor + 1, 0)
end
---@return Semver
function methods:nextPatch()
return semver(self.major, self.minor, self.patch + 1)
end

---@class Semver : ISemver
---@field major integer
---@field minor integer
---@field patch integer
---@field prerelease? string
---@field build? string
local mt = { __index = methods }
function mt:__eq(other)
return self.major == other.major and
Expand Down Expand Up @@ -188,6 +198,7 @@ function mt:__tostring()
return table.concat(buffer)
end

---@return Semver
local function new(major, minor, patch, prerelease, build)
assert(major, "At least one parameter is needed")

Expand Down
30 changes: 29 additions & 1 deletion tests/mason-core/installer/managers/npm_spec.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
local Result = require "mason-core.result"
local installer = require "mason-core.installer"
local match = require "luassert.match"
local npm = require "mason-core.installer.managers.npm"
local spawn = require "mason-core.spawn"
local stub = require "luassert.stub"

describe("npm manager", function()
it("should init package.json", function()
local ctx = create_dummy_context()
stub(ctx.fs, "append_file")
stub(spawn, "npm")
spawn.npm.returns(Result.success {})
spawn.npm.on_call_with({ "version", "--json" }).returns(Result.success {
stdout = [[ { "npm": "8.1.0" } ]],
})
installer.exec_in_context(ctx, function()
npm.init()
end)
Expand All @@ -18,7 +25,28 @@ describe("npm manager", function()
"--scope=mason",
}
assert.spy(ctx.fs.append_file).was_called(1)
assert.spy(ctx.fs.append_file).was_called_with(match.is_ref(ctx.fs), ".npmrc", "global-style=true")
assert.spy(ctx.fs.append_file).was_called_with(match.is_ref(ctx.fs), ".npmrc", "\nglobal-style=true")
end)

it("should use install-strategy on npm >= 9", function()
local ctx = create_dummy_context()
stub(ctx.fs, "append_file")
stub(spawn, "npm")
spawn.npm.returns(Result.success {})
spawn.npm.on_call_with({ "version", "--json" }).returns(Result.success {
stdout = [[ { "npm": "9.1.0" } ]],
})
installer.exec_in_context(ctx, function()
npm.init()
end)

assert.spy(ctx.spawn.npm).was_called(1)
assert.spy(ctx.spawn.npm).was_called_with {
"init",
"--yes",
"--scope=mason",
}
assert.spy(ctx.fs.append_file).was_called_with(match.is_ref(ctx.fs), ".npmrc", "\ninstall-strategy=shallow")
end)

it("should install", function()
Expand Down