diff --git a/README.md b/README.md index 5ccba76..99de52c 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ By default, the plugin uses the default `tsc` command with the `--noEmit` flag t use_trouble_qflist = false, use_diagnostics = false, run_as_monorepo = false, + monorepo_root_patterns = { ".git" }, max_tsconfig_files = 20, bin_path = nil, bin_name = "tsc", diff --git a/lua/tsc/init.lua b/lua/tsc/init.lua index 0853b8a..8cdf647 100644 --- a/lua/tsc/init.lua +++ b/lua/tsc/init.lua @@ -17,6 +17,7 @@ end --- @field use_trouble_qflist? boolean - (false) When true the quick fix list will be opened in Trouble if it is installed --- @field use_diagnostics? boolean - (false) When true the errors will be set as diagnostics --- @field run_as_monorepo? boolean - (false) When true the `tsc` process will be started mode for each tsconfig in the current working directory +--- @field monorepo_root_patterns? string[] - ({".git"}) The file pattern to be used to find the root of the monorepo --- @field max_tsconfig_files? number - (20) Will not run `tsc` if number of found tsconfig files is greater. --- @field bin_path? string - Path to the tsc binary if it is not in the projects node_modules or globally --- @field bin_name? string - Name of the binary to use (default: "tsc") @@ -39,6 +40,7 @@ local DEFAULT_CONFIG = { enable_progress_notifications = true, enable_error_notifications = true, run_as_monorepo = false, + monorepo_root_patterns = { ".git" }, max_tsconfig_files = 20, flags = { noEmit = true, @@ -83,7 +85,11 @@ local function format_notification_msg(msg, spinner_idx) return string.format(" %s %s ", config.spinner[spinner_idx], msg) end -M.run = function() +--- @class RunParams +--- @field force_monorepo_mode boolean + +--- @param params RunParams? +M.run = function(params) -- Closed over state local tsc = config.bin_path or utils.find_tsc_bin(config.bin_name) local errors = {} @@ -107,7 +113,14 @@ M.run = function() return end - local configs_to_run = utils.find_tsconfigs(config.run_as_monorepo) + --- @type boolean + local run_as_monorepo = (params and params.force_monorepo_mode) or config.run_as_monorepo or false + + -- When running a monorepo check, we need to make sure we run the commands + -- from the root of the monorepo, as the cwd may be a subdirectory + local monorepo_root_path = vim.fs.root(0, config.monorepo_root_patterns) or "." + + local configs_to_run = utils.find_tsconfigs(run_as_monorepo, monorepo_root_path) if #configs_to_run > 0 and not config.run_as_monorepo then M.stop() @@ -240,8 +253,10 @@ M.run = function() ) end + ---@param output string[] + ---@param project string local function on_stdout(output, project) - local result = utils.parse_tsc_output(output, config) + local result = utils.parse_tsc_output(output, config, project) running_processes[project].errors = result.errors @@ -277,6 +292,7 @@ M.run = function() end end + --- @param project string local opts = function(project) return { on_stdout = function(_, output) @@ -286,6 +302,7 @@ M.run = function() on_exit() end, stdout_buffered = true, + cwd = vim.fs.dirname(project), } end @@ -338,6 +355,13 @@ function M.setup(opts) M.run() end, { desc = "Run `tsc` asynchronously and load the results into a qflist", force = true }) + vim.api.nvim_create_user_command("TSCMono", function() + M.run({ force_monorepo_mode = true }) + end, { + desc = "Run `tsc` for all the typescript projects in the monorepo", + force = true, + }) + vim.api.nvim_create_user_command("TSCStop", function() M.stop() vim.notify_once(format_notification_msg("TSC stopped"), nil, get_notify_options()) diff --git a/lua/tsc/utils.lua b/lua/tsc/utils.lua index ded3919..8ae9ddf 100644 --- a/lua/tsc/utils.lua +++ b/lua/tsc/utils.lua @@ -16,15 +16,16 @@ M.find_tsc_bin = function(bin_name) local node_modules_binary = vim.fn.findfile("node_modules/.bin/" .. bin_name, ".;") if node_modules_binary ~= "" then - return node_modules_binary + return vim.fs.abspath(node_modules_binary) end return bin_name end --- @param run_mono_repo boolean ---- @return table -M.find_tsconfigs = function(run_mono_repo) +--- @param monorepo_root_path string +--- @return string[] +M.find_tsconfigs = function(run_mono_repo, monorepo_root_path) if not run_mono_repo then return M.find_nearest_tsconfig() end @@ -33,9 +34,15 @@ M.find_tsconfigs = function(run_mono_repo) local found_configs = nil if M.is_executable("rg") then - found_configs = vim.fn.system("rg -g '!node_modules' --files | rg 'tsconfig.*.json'") + found_configs = vim.trim( + vim.system({ "rg", "--files", "-g", "!node_modules", "-g", "tsconfig.json", monorepo_root_path }):wait().stdout + ) else - found_configs = vim.fn.system('find . -not -path "*/node_modules/*" -name "tsconfig.*.json" -type f') + found_configs = vim.trim( + vim + .system({ "find", monorepo_root_path, "-not", "-path", "*/node_modules/*", "-name", "tsconfig.*.json", "-type", "f" }) + :wait().stdout + ) end if found_configs == nil then @@ -43,7 +50,7 @@ M.find_tsconfigs = function(run_mono_repo) end for s in found_configs:gmatch("[^\r\n]+") do - table.insert(tsconfigs, s) + table.insert(tsconfigs, vim.fs.abspath(s)) end assert(tsconfigs) @@ -51,10 +58,10 @@ M.find_tsconfigs = function(run_mono_repo) end M.find_nearest_tsconfig = function() - local tsconfig = vim.fn.findfile("tsconfig.json", ".;") + local tsconfig_root = vim.fs.root(0, "tsconfig.json") - if tsconfig ~= "" then - return { tsconfig } + if tsconfig_root then + return { vim.fs.joinpath(vim.fs.abspath(tsconfig_root), "tsconfig.json") } end return {} @@ -96,7 +103,10 @@ M.parse_flags = function(flags) return parsed_flags end -M.parse_tsc_output = function(output, config) +---@param output string[] +---@param config Opts +---@param project string +M.parse_tsc_output = function(output, config, project) local errors = {} local files = {} @@ -104,6 +114,8 @@ M.parse_tsc_output = function(output, config) return { errors = errors, files = files } end + local project_root = vim.fs.dirname(project) + for _, line in ipairs(output) do local filename, lineno, colno, message = line:match("^(.+)%((%d+),(%d+)%)%s*:%s*(.+)$") if filename ~= nil then @@ -112,7 +124,7 @@ M.parse_tsc_output = function(output, config) text = better_messages.translate(message) end table.insert(errors, { - filename = filename, + filename = vim.fs.joinpath(project_root, filename), lnum = tonumber(lineno), col = tonumber(colno), text = text,