lua

luabundle

A library for bundling several Lua files into a single file.

Showing:

Popularity

Downloads/wk

91

GitHub Stars

10

Maintenance

Last Commit

4mos ago

Contributors

4

Package

Dependencies

2

Size (min+gzip)

11.1KB

License

MIT

Type Definitions

Built-In

Tree-Shakeable

No?

Categories

Readme

luabundle

A library for bundling several Lua files into a single file.

If you're after a CLI tool, please see luabundler, which utilises this library.

Version Downloads/week License

Installation

Install the node module with:

yarn add luabundle

or with NPM:

npm install --save luabundle

Usage

import { bundle } from 'luabundle'

const bundledLua = bundle('./file.lua')
// `bundledLua` now contains valid Lua which can be written straight to disk, stdout etc.

If you're using TypeScript, TS definitions are available by default.

Bundling

In order to create a bundle, any referenced files will be loaded from disk. However, the root module (entry-point Lua) may be provided either as a file path (bundle), or as a string (bundleString).

bundle(inputFilePath: string, options: BundleOptions) => string

Reads a Lua file, all recursively require()d modules, and returns the resultant bundle as a string.

bundleString(lua: string, options: BundleOptions) => string

Loads all modules require()d in the provided Lua string, and returns the resultant bundle as a string.

Bundle Options

ParameterTypeDefaultDescription
expressionHandlerExpressionHandlerundefinedSee Expression Handler
forcebooleanfalseWhether the provided Lua should always be returned as a bundle, even when it required no other modules.
identifiersIdentifiersSee Identifiers"""
isolatebooleanfalseBy default, the bundle is not isolated i.e. at runtime we'll try fallback to regular require() for modules not included in the bundle.
luaVersion"5.1" \| "5.2" \| "5.3" \| "LuaJIT""5.3"
metadatabooleantrueUnless set to false, the bundle will be encoded with metadata (Lua comments) that describe the specification of the bundle. Unbundling is only possible for bundles that are bundled with metadata.
pathsstring[]['?', '?.lua']See Search Paths
postprocess(module: Module, options: RealizedOptions) => stringundefinedPostprocess a module, immediately before its added to the bundle.
preprocess(module: Module, options: RealizedOptions) => stringundefinedPreprocess a module, before luabundle makes any of its own modifications.
rootModuleNamestring"__root"The contents of inputFilePath are interpreted as module with this name.

RealizedOptions refers to these Options after all default values have been merged i.e. identifiers is guaranteed to exist etc.

Module refers to an object of the form:

type Module = {
    name: string,
    resolvedPath?: string,
    content: string,
}

Search Paths

In order to know where to look for required files, Lua search path patterns are utilised.

In addition to allowing you to add directories to search within, the pattern format allows you to configure supported file extensions as well.

Files will be resolved relative these paths. If a specified path is itself relative, that path will be resolved relative to the current working directory.

The default behaviour (paths option omitted) is to resolve module names relative to the current working directory, considering files with a .lua extension or no extension at all.

Expression Handler

type ExpressionHandler = (module: Module, expression: Expression) => string | string[] | null | undefined | void

Expression is a moonsharp-luaparse expression.

Module is as described above.

By default, luabundle can only resolve string literal requires. When a require() call is encountered that's some other expression e.g.

require(someVariable)

then luabundle cannot determine which modules may be dynamically required, thus the require call is simply ignored.

This will work just fine at runtime if someVariable refers to a module that's in the bundle. However, luabundle doesn't know which modules it should add to bundle, and by default won't add any (for this require() call).

The simplest way to handle this situation is just to log a warning e.g.

import bundle from 'luabundle'

const bundledLua = bundle('./file.lua', {
    expressionHandler: (module, expression) => {
        const start = expression.loc.start
        console.warn(`WARNING: Non-literal require found in '${module.name}' at ${start.line}:${start.column}`)
    },
})

However, if you know at bundle time a list of modules that you want to include for dynamic requires you can do something like:

import bundle from 'luabundle'

const bundledLua = bundle('./file.lua', {
    expressionHandler: () => ['moduleA', 'moduleB'],
})

In this case the generated require() call won't be altered in any way, however luabundle will simply resolve moduleA and moduleB and add them to the bundle.

Alternatively, if a module name is returned as a string (not a string[]), luabundle will substitute out the dynamic expression for a string literal, resolve the module, and ensure it's included in the bundle.

Identifiers

This is an advanced feature. Under normal use you shouldn't need to worry about any of this.

type Identifiers = {
    register: string,
    require: string,
    loaded: string,
    modules: string,
}

Generated bundles contain a few local scoped identifiers which are accessible as upvalues throughout the entire bundle (i.e. in every module).

IdentifierDefault
register"__bundle_register"
require"__bundle_require"
modules"__bundle_modules"
loaded"__bundle_loaded"

If for example, at runtime you want to get a list of all modules included in the bundle, you can iterate through the keys in "modules table", by default accessible as __bundle_modules.

Unbundling

If a bundle was generated with metadata (default), then luabundle is also able to unbundle it. If an attempt is made to unbundle a file that does not contain bundle metadata, an error will be thrown.

Because Lua (and luabundle) utilise Search Paths when creating the bundle, modules could have come from many different locations. This information is intentionally not encoded in the bundle, thus whilst we'll output a valid module directory structure, it may not match the original.

unbundle(inputFilePath: string, options: Options) => UnbundledData

Reads a bundle file, and returns all modules contained within, unless the rootOnly is true, in which case other modules will be ignored.

unbundleString(lua: string, options: Options) => UnbundledData

Returns all modules contained within the specified Lua string, unless the rootOnly is true, in which case other modules will be ignored.

Unbundled Data

type UnbundledData = {
    metadata: Metadata,
    modules: ModuleMap,
}

type ModuleMap = {
    [name: string]: Module,
}

type Module = {
    name: string,
    content: string,
    start: FilePosition,
    end: FilePosition,
}

type FilePosition = {
    index: number,
    line: number,
    column: number,
}

In addition to name and content. each module also has a start and end, which describe where the module is located within the provided bundle.

Metadata is described below.

Unbundle Options

ParameterTypeDefaultDescription
rootOnlybooleanfalseWhen set to true, only the root module of the bundle will be processed and returned.

RealizedOptions refers to these Options after all default values have been merged i.e. identifiers is guaranteed to exist etc.

Module is the same type encountered when bundling.

Metadata

Unless disabled when bundling, bundles are generated with some metadata that is necessary to unbundle.

With the exception of version, these values/types correspond with the types described above. version is simply the version of luabundle that generated the bundle.

type Metadata = {
    identifiers: Identifiers,
    luaVersion: string,
    rootModuleName: string,
    version: string,
}

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100