recursiveRequire

recursiveRequire is a function which can be used to require all the Lua files in a folder, and all the Lua files in folders within that folder, and Lua files in folders within those folders, and so on.

Usage example

If there is a folder named levels in the same directory as main.lua, and it contains the files level1.lua, level2.lua, and a folder named boss was also inside levels and it contains two files named level1.lua and level2.lua as well, we could call recursiveRequire from main.lua like this:

recursiveRequire('levels')

And it would be equivalent to this:

require('levels.level1')
require('levels.level2')
require('levels.boss.level1')
require('levels.boss.level2')

(Notice how the paths given to require have folders separated by dots, not slashes, and there is no ".lua" at the end of the filenames.)

The function

What this function does is:

  • Takes a folder as an argument.
  • Loops through the files and folders in the folder it is given.
  • And if what's found in this loop is a...
    • Folder: run the recursiveRequire function on this newly found folder.
    • File which ends in ".lua": require it.
function recursiveRequire(folder)
   for _, file in ipairs(love.filesystem.getDirectoryItems(folder)) do
      local path = folder..'/'..file
      if love.filesystem.isDirectory(path) then
         recursiveRequire(path)
      else
         local pathWithoutDotLua = path:match('(.*)%.lua$')
         if pathWithoutDotLua then
            require(pathWithoutDotLua:gsub('/', '.'))
         end
      end
   end
end

A line-by-line explanation

function recursiveRequire(folder)

love.filesystem.getDirectoryItems returns a table containing all the names of the files and folders in the folder that is given as its argument.

ipairs loops through this table, and the path of the current file/folder is stored in the variable file.

   for _, file in ipairs(love.filesystem.getDirectoryItems(folder)) do
      local path = folder..'/'..file

love.filesystem.isDirectory returns true if the string it is given is the name of a folder, or false otherwise.

      if love.filesystem.isDirectory(path) then

If the path is a folder, recursiveRequire is called for this newly found folder.

Notice how the definition of recursiveRequire actually has a call to itself within it; this function is recursive.

If this seems strange because it appears that the function is being called before it is finished being defined, note that what is written here is only the definition of the function, and the function is only actually run afterwards, when it is called.

         recursiveRequire(path)

If the path isn't a folder, and is therefore a file...

      else

The path is checked to see if it ends with a .lua, and if it does, the path without the .lua at the end is stored.

Strings have methods, which are equivalent to the functions in the string library. For example, path:match('(.*)%.lua$') is equivalent to string.match(path, '(.*)%.lua$')

The match function in the string library returns parts of the string by matching patterns.

The pattern used here tries to match a string which ends with .lua, and returns any characters before that.

Here's how it works:

path:match('(.*).lua$')

Start capture
All characters, zero or more of them
End capture
The string .lua (% is the escape character needed for the literal character ".")
Ends with that (.lua)

If the pattern isn't found (i.e. the filename doesn't end with .lua) then nil is returned.

         local pathWithoutDotLua = path:match('(.*)%.lua$')

If the path does end with .lua, this if block is run.

Anything in a conditional statement (if or while or until) which isn't nil or false is considered to be true.

If path:match('(.*)%.lua$') returned anything (which would mean that path ends with .lua), pathWithoutDotLua won't be nil.

         if pathWithoutDotLua then

All of the slashes in the path (without ".lua" at the end) are replaced with dots, and it's given as an argument to require.

The gsub method from the string library returns a string which has all of the occurences of the pattern in the first argument replaced by the string in the second argument.

Note: It's common for require to be used in a way which looks like require 'file'. Since require is a function, where are the parentheses?

If a function takes a single literal string as an argument, the parentheses can be omitted (for another example, print 'Example!' is equivalent to print('Example!')).

recursiveRequire can be called this way too, for example recursiveRequire 'code.libraries'

            require(pathWithoutDotLua:gsub('/', '.'))
         end
      end
   end
end



HTML generated by Locco.

Copyright is waived on all original parts of this code walkthrough.