npm
Source
Description
@wolfstar/kit provides defineModule and the runtime that installs Stars modules, the counterpart of @nuxt/kit.
A module is a package that a project lists in modules in stars.config. While the stars CLI
loads the project, it installs each module. A module can register runtime plugins in the bot, add auto import
presets, add directories of pieces and locales, run code at the client’s lifecycle points, and hook into the CLI.
@wolfstar/kit depends on @wolfstar/schema only. A module declares it as a peer
dependency without pulling in the framework or the CLI.
When to use it
- Use
modulesinstars.configto activate a module, such as the@wolfstar/plugin-*packages. You do not import@wolfstar/kityourself for this, but the plugin packages list it as an optional peer, so install it in the project. - Depend on
@wolfstar/kitwhen you write a package that should be installable fromstars.config.
Activating modules
List each module as a package specifier, or as a[specifier, options] tuple to pass options. Falsy entries are
dropped, so condition && 'some-module' works:
defaults: plain objects merge deeply, and arrays and other values replace
the default. A module that declares a meta.configKey also reads its options
from a top-level stars.config key of that name. A package listed in modules is no longer activated through the /register import the CLI otherwise
injects for @wolfstar/plugin-* packages. With
future.compatibilityVersion: 7, the CLI injects no /register
import at all, so only modules register.
Writing a module
Export the module as the package’s default export withdefineModule. Like definePlugin, it is an identity helper
that types the definition.
definePlugin export. When the
export is a function, the module runtime calls it with the options given to addPlugin:
Module definition
meta.compatibility.framework is a semver range checked against the project’s installed @wolfstar/http-framework,
and meta.compatibility.stars against the running @wolfstar/cli. A mismatch fails the install early instead of
leaving a plugin that never runs. A range is only checked when the CLI finds the version.
Reading options from a config key
Setmeta.configKey to let users configure the module from a top-level key of stars.config, instead of only through
the [specifier, options] tuple in modules. The options setup receives are merged in this order, each layer over
the previous one:
- The module’s
defaults. - The object under
configKeyinstars.config. - The inline options: the
[specifier, options]tuple, or the options passed toctx.installModule.
defineConfig, augment the StarsConfig interface of @wolfstar/schema:
stars prepare does not reference installed
modules yet, so import the package in stars.config.ts.
A configKey must follow these rules, or the install fails with MODULE_INVALID:
- It is a non-empty string.
- It is not a built-in
stars.configkey:root,entry,build,dev,codegen,imports,env,hooks,modules,experimental,future,vite, ortsdown. - No other installed module claims it.
- The value under it in
stars.config, when set, is a plain object.
dependencies or ctx.installModule read their key too. Once every module is set up, the
CLI reports any top-level key that no installed module claimed as
UNKNOWN_OPTION, so a misspelled key is still an error.
A module is installed once, by meta.name. When two paths install the same module, the first installation wins,
whatever options the second one passes.
The setup context
The plugin runs in the bot’s process, while
setup runs in the CLI’s. That is why addPlugin takes a plugin
source instead of a plugin value: a string or URL, or { from, export?, options? }.
fromis a package specifier resolved from the project, an absolute path, or afile:URL such asnew URL('./plugin.js', import.meta.url). A relative path is rejected, because it would resolve differently in each build tool.exportnames the export to use and defaults to'default'. When the export is a function, the runtime calls it as a factory withoptions. A legacyPluginclass is passed toClient.useas it is, never called.optionsmust be JSON-serialisable, because the CLI writes it into the built entry.
Adding pieces
ctx.addPiecesDir(directory) registers a directory of pieces that the bot loads next to its own. Use it to ship
commands, listeners, interaction handlers, or preconditions in a module. The directory holds one folder per store
(commands, listeners, interaction-handlers, preconditions), like the bot’s own src:
- Give an absolute path or a
file:URL. A relative path fails the install withMODULE_PIECES_INVALID. - The pieces are read from files at runtime, so the directory has to exist in the built bot. A build that bundles
everything (Vite, Nitro) has no such directory, so
addPiecesDiris not useful there. - Only the stores that exist when the module runtime runs are covered: the framework’s four, and any registered by an earlier import. A folder for a store that a plugin registers later is ignored.
Adding locales
ctx.addLocales(directory) registers a directory of translations next to the project’s own languages directory. Use it
when the pieces or plugins of a module need their own strings. The directory is laid out as
<language>/<namespace>.json, and nested folders make namespaces such as commands/shared:
- Give an absolute path or a
file:URL to a directory that exists when the module is set up.setupreads its namespaces then. - A runtime plugin generated by the CLI merges the directory into the client’s
i18noption before@wolfstar/plugin-i18nextbuilds its handler. The project’s own languages directory is kept. - It needs
@wolfstar/plugin-i18next, and a framework that re-exportsgetRootDatafrom@wolfstar/http-framework. - Like
addPiecesDir, the absolute path is written into the generated source and read at runtime. A build that bundles everything (Vite, Nitro), or one moved to another machine, does not find the directory. - A relative path, or a directory that cannot be read, fails the install with
MODULE_LOCALES_INVALID.
Runtime client hooks
A module can run code in the bot at the client’s lifecycle points, without writing a full runtime plugin. Declare the hook inhooks with a client: name, or call ctx.addClientHook(name, source) from setup. The supported names are:
client:preGenericsInitializationclient:preInitializationclient:postInitializationclient:preLoadclient:postListen
setup runs in the CLI’s, so it is given by source like a plugin: a package
specifier, an absolute path or a file: URL, or { from, export? }. The export (default 'default') is a function
called with (client, clientOptions), the same arguments a definePlugin hook gets.
- Only
client:preLoadandclient:postListenhooks may be async. The other three run inside the synchronousClientconstructor. - A module’s hooks share one runtime plugin named after the module, so a failing hook is reported with the module’s name.
- A module declares one hook per name. A second hook with the same name, an unknown
client:name, or a hook that is not given by source (such as a function, or a relative path) fails the install withMODULE_HOOK_INVALID.
Production with tsc or none
The tsdown, vite, and Nitro builds register the runtime plugins through the bot’s entry. The tsc and none
build tools cannot transform the entry, so the plugins, pieces directories, locales, and client hooks are preloaded
with node --import instead:
stars devpreloads them for you.stars prepareandstars buildwrite.stars/modules.mjs. Start the production bot with it preloaded:
env is enabled, .stars/modules.mjs imports .stars/env.mjs first, so the environment is loaded before any
plugin is evaluated. stars build, stars prepare, and stars info report a
MODULES_PRELOAD_REQUIRED warning for such projects.
Errors
When a module cannot be installed,@wolfstar/kit throws a ModuleError. Its moduleName is the module’s
meta.name, or the specifier it was listed by, and its code is one of:
The
stars CLI reports a failing module as a MODULE_FAILED diagnostic
and exits with code 2.
Programmatic usage
setupModules({ config, hooks, load, versions }) is the function the CLI calls. load imports a specifier from the
project, and hooks is a hookable-shaped registry. The result, a ModulesRuntime, lists the installed modules and
the runtime plugins, pieces directories (piecesDirs), locales, client hooks (clientHooks), import presets, and
configKeys they contributed. mergeOptions(defaults, options) exposes the merge used for
module options.