--- Build strict file-reference rules shared by JavaScript-family formats
-- @module formats.js.references

local javascript_references = {}

-- Pattern breakdown: standalone language words end immediately before a quoted module specifier
local from_prefix = "%f[%w_%.:]from%f[^%w_%.:]%s*"
local static_import_prefix = "%f[%w_%.:]import%f[^%w_%.:]%s+"
local dynamic_import_prefix = "%f[%w_%.:]import%f[^%w_%.:]%s*%(%s*"
local require_prefix = "%f[%w_%.:]require%f[^%w_%.:]%s*%(%s*"

--- Return whether one module specifier is an explicit relative path
-- @param reference table parsed file reference
-- @return boolean accepted
local function is_relative_module(reference)
	return reference.path:match("^%.[/\\]") ~= nil
		or reference.path:match("^%.%.[/\\]") ~= nil
end

--- Return whether one path already has a filename extension
-- @param filename string local module filename
-- @return boolean has_extension
local function has_extension(filename)
	return filename:match("[^/\\]+%.[^/\\]+$") ~= nil
end

--- Build one module-candidate callback for a format's preferred extensions
-- @param extensions table ordered extensions including their leading dots
-- @return function candidates
local function build_candidates(extensions)
	return function(reference)
		local filename = reference.path:match("^[^%?#]+")
		if not filename then
			return {}
		end
		if has_extension(filename) then
			return { filename }
		end

		local candidates = { filename }
		for _, extension in ipairs(extensions) do
			candidates[#candidates + 1] = filename .. extension
		end
		for _, extension in ipairs(extensions) do
			candidates[#candidates + 1] = filename .. PATHSEP .. "index" .. extension
		end

		return candidates
	end
end

--- Return strict relative-module rules for one JavaScript-family format
-- @param extensions table ordered module extensions including their leading dots
-- @return table rules
function javascript_references.new(extensions)
	local candidates = build_candidates(extensions)
	local rules = {}

	for _, prefix in ipairs({
		from_prefix,
		static_import_prefix,
		dynamic_import_prefix,
		require_prefix,
	}) do
		for _, quote in ipairs({ "\"", "'" }) do
			rules[#rules + 1] = {
				prefix = prefix,
				open = quote,
				close = quote,
				validate = is_relative_module,
				candidates = candidates,
			}
		end
	end

	return rules
end

return javascript_references
