Files
metabuilder/packages/package_validator/seed/scripts/structure_validator.lua

285 lines
7.9 KiB
Lua

-- Package folder structure validation
local M = {}
-- Expected package structure
M.REQUIRED_STRUCTURE = {
["seed/metadata.json"] = true,
["seed/components.json"] = true
}
M.OPTIONAL_STRUCTURE = {
["seed/scripts/"] = "directory",
["seed/scripts/init.lua"] = "file",
["seed/scripts/tests/"] = "directory",
["static_content/"] = "directory",
["static_content/icon.svg"] = "file",
["README.md"] = "file",
["examples/"] = "directory"
}
-- Validate basic folder structure
function M.validate_structure(package_path)
local errors = {}
local warnings = {}
-- Check required files
for path, _ in pairs(M.REQUIRED_STRUCTURE) do
local full_path = package_path .. "/" .. path
local file = io.open(full_path, "r")
if not file then
table.insert(errors, "Required file missing: " .. path)
else
file:close()
end
end
-- Check optional but recommended files
for path, type in pairs(M.OPTIONAL_STRUCTURE) do
local full_path = package_path .. "/" .. path
if type == "file" then
local file = io.open(full_path, "r")
if not file then
table.insert(warnings, "Recommended file missing: " .. path)
else
file:close()
end
elseif type == "directory" then
-- Note: Directory checking would be done with OS-specific commands
-- This is a placeholder for the pattern
end
end
return errors, warnings
end
-- Validate scripts directory structure
function M.validate_scripts_structure(package_path, metadata)
local errors = {}
local warnings = {}
local scripts_path = package_path .. "/scripts"
-- Check if scripts directory exists when exports.scripts is defined
if metadata.exports and metadata.exports.scripts and #metadata.exports.scripts > 0 then
local dir_exists = false
local test_file = io.open(scripts_path .. "/init.lua", "r")
if test_file then
test_file:close()
dir_exists = true
end
if not dir_exists then
table.insert(errors, "scripts/ directory required when exports.scripts is defined")
end
-- Check for init.lua
local init_file = io.open(scripts_path .. "/init.lua", "r")
if not init_file then
table.insert(warnings, "scripts/init.lua is recommended as entry point")
else
init_file:close()
end
-- Check for tests directory if lua_test is a devDependency
local has_lua_test = false
if metadata.devDependencies then
for _, dep in ipairs(metadata.devDependencies) do
if dep == "lua_test" then
has_lua_test = true
break
end
end
end
if has_lua_test then
-- lua_test is a devDependency, so tests are expected
local test_init = io.open(scripts_path .. "/tests/metadata.test.lua", "r")
if not test_init then
table.insert(warnings, "scripts/tests/ directory with test files recommended when lua_test is a devDependency")
else
test_init:close()
end
end
end
return errors, warnings
end
-- Validate static content structure
function M.validate_static_content(package_path, metadata)
local errors = {}
local warnings = {}
if metadata.icon then
local icon_path = package_path .. "/" .. metadata.icon
local icon_file = io.open(icon_path, "r")
if not icon_file then
table.insert(errors, "Icon file not found: " .. metadata.icon)
else
icon_file:close()
end
else
table.insert(warnings, "No icon defined in metadata")
end
return errors, warnings
end
-- Check for orphaned files (files not referenced in metadata)
function M.check_orphaned_files(package_path, metadata)
local warnings = {}
-- This would scan the package directory and check if files are referenced
-- Placeholder for the pattern
return warnings
end
-- Validate naming conventions
function M.validate_naming_conventions(package_path, metadata)
local errors = {}
local warnings = {}
-- Package directory should match packageId
local dir_name = package_path:match("([^/]+)$")
if dir_name ~= metadata.packageId then
table.insert(errors, "Package directory name '" .. dir_name .. "' does not match packageId '" .. metadata.packageId .. "'")
end
-- Check script file naming
if metadata.exports and metadata.exports.scripts then
for _, script_name in ipairs(metadata.exports.scripts) do
-- Script names should be lowercase with underscores
if not string.match(script_name, "^[a-z_]+$") then
table.insert(warnings, "Script name '" .. script_name .. "' should use lowercase and underscores only")
end
end
end
-- Check component naming
if metadata.exports and metadata.exports.components then
for _, component_name in ipairs(metadata.exports.components) do
-- Component names can be PascalCase or snake_case
local is_valid = string.match(component_name, "^[A-Z][a-zA-Z]+$") or
string.match(component_name, "^[a-z_]+$")
if not is_valid then
table.insert(warnings, "Component name '" .. component_name .. "' should use PascalCase or snake_case")
end
end
end
return errors, warnings
end
-- Validate test files structure
function M.validate_test_structure(package_path, metadata)
local errors = {}
local warnings = {}
-- Check if lua_test is a devDependency
local has_lua_test = false
if metadata.devDependencies then
for _, dep in ipairs(metadata.devDependencies) do
if dep == "lua_test" then
has_lua_test = true
break
end
end
end
if not has_lua_test then
-- No lua_test, tests are optional
return errors, warnings
end
local tests_path = package_path .. "/scripts/tests"
-- Check for common test files
local common_tests = {
"metadata.test.lua",
"components.test.lua"
}
local found_tests = false
for _, test_file in ipairs(common_tests) do
local test_path = tests_path .. "/" .. test_file
local file = io.open(test_path, "r")
if file then
file:close()
found_tests = true
end
end
if not found_tests then
table.insert(warnings, "No test files found (metadata.test.lua, components.test.lua) despite lua_test devDependency")
end
return errors, warnings
end
-- Validate complete package structure
function M.validate_package_structure(package_path, metadata)
local results = {
valid = true,
errors = {},
warnings = {}
}
-- Basic structure
local struct_errors, struct_warnings = M.validate_structure(package_path)
for _, err in ipairs(struct_errors) do
table.insert(results.errors, err)
results.valid = false
end
for _, warn in ipairs(struct_warnings) do
table.insert(results.warnings, warn)
end
-- Scripts structure
local script_errors, script_warnings = M.validate_scripts_structure(package_path, metadata)
for _, err in ipairs(script_errors) do
table.insert(results.errors, err)
results.valid = false
end
for _, warn in ipairs(script_warnings) do
table.insert(results.warnings, warn)
end
-- Static content
local static_errors, static_warnings = M.validate_static_content(package_path, metadata)
for _, err in ipairs(static_errors) do
table.insert(results.errors, err)
results.valid = false
end
for _, warn in ipairs(static_warnings) do
table.insert(results.warnings, warn)
end
-- Naming conventions
local naming_errors, naming_warnings = M.validate_naming_conventions(package_path, metadata)
for _, err in ipairs(naming_errors) do
table.insert(results.errors, err)
results.valid = false
end
for _, warn in ipairs(naming_warnings) do
table.insert(results.warnings, warn)
end
-- Test structure (only if lua_test is a devDependency)
local test_errors, test_warnings = M.validate_test_structure(package_path, metadata)
for _, err in ipairs(test_errors) do
table.insert(results.errors, err)
results.valid = false
end
for _, warn in ipairs(test_warnings) do
table.insert(results.warnings, warn)
end
return results
end
return M