Skip to content

Writing a package ​

A package is just a project whose entry gets imported instead of run. There is no special format and no name field: the project that uses it picks the import name.

The layout ​

text
mathx/
  thicket.json
  src/
    index.tks
    easing.tks
json5
// mathx/thicket.json
{
    entry: "src/index.tks",
}
ts
// mathx/src/index.tks
export function lerp(a: float, b: float, t: float): float {
    return a + (b - a) * t;
}

export function clamp01(t: float): float {
    if (t < 0) {
        return 0;
    }
    if (t > 1) {
        return 1;
    }
    return t;
}

Importing the package loads the entry's exports. It never calls main. A consumer reaches other files by path, relative to the package root: import { easeIn } from "mathx/src/easing";.

A package cannot import anything outside its own folder.

Using it ​

ts
// game/app.tks, with packages: { mathx: "../mathx" }
import { clamp01, lerp } from "mathx";

console.log(lerp(0, 10, clamp01(0.25)));

Tests are a sub-project ​

Put the tests in a folder with its own thicket.json that lists the package by a relative path. They import it exactly as a real consumer would:

json5
// mathx/tests/thicket.json
{ entry: "all.tks", packages: { mathx: ".." } }

There is no thicket test command yet. Run the tests with thicket run.

Declare what you need ​

List what the package uses. These are requirements, not grants:

json5
{
    entry: "src/index.tks",
    systems: ["willow"],
    permissions: { fs: true },
}

A project that uses your package has to list the same systems and turn on the same permissions. It gets a clear error naming the fix if it does not.

A package whose code uses engine makes its consumer a window app. Code that only needs the scene can read Scene.main instead, which is null in a console program and imposes no window.

Reach your own files with moduleDir ​

A relative path given to fs resolves against the working directory, which is the game's folder, not yours. To load your own fonts, images or data, build the path from moduleDir, the folder the current source file lives in:

ts
import { Files } from "fs";

export async function loadTheme(): Promise<string> {
    return await Files.readText(Files.join(moduleDir, "theme.json"));
}

moduleDir needs no import. It is right for a local folder, a package in thicket_packages/ and a built program alike. Reading through it is an ordinary file read, so the package declares fs and the consuming project grants it.

In a built program, a consumer ships your files by listing them as "<package>:<path>" in its assets.

Offer tools ​

A package can offer script tools to the projects that use it. Map a tool name to a script in tools:

json5
{
    entry: "src/index.tks",
    tools: { themec: "tools/themec.tks" },
}

A project that lists the package runs it with thicket tool themec, and thicket tool lists every tool available.

Share it ​

Publish the folder as a git repository and tag a version. Users add it with a URL pinned to that tag, like "https://github.com/you/mathx-tks#v1.0.0". There is no package registry.