Skip to content

Commit

Permalink
fix(npm): set install-strategy on npm >= 9
Browse files Browse the repository at this point in the history
Closes #1175.
  • Loading branch information
williamboman committed Apr 4, 2023
1 parent dbf34ee commit c17b861
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 5 deletions.
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

0 comments on commit c17b861

Please sign in to comment.