API reference·skills/ignifx/references/api/vite-plugin.md
@ignifx/vite-plugin
@ignifx/vite-plugin public barrel: asset manifest generation, scene and prefab JSON validation,
WASM and asset handling, and HMR hooks (docs/architecture/00-overview.md §2).
The package depends on nothing from the engine — it is a build-time tool that produces the files
@ignifx/core consumes at runtime. Explicit named re-exports only, no export *
(coding standards §4), and no default export: the plugin is ignifx.
Example#
// vite.config.tsimport { defineConfig } from "vite";import { ignifx } from "@ignifx/vite-plugin";export default defineConfig({ plugins: [ignifx()] });Classes#
VitePluginError#
The error every @ignifx/vite-plugin API throws for a failure it can describe
(CONSTITUTION.md §3.9). It always carries a stable IGX-#### code so that a build log stays
machine-readable even when the message is truncated.
Example#
try { await scanAssets({ assetRoot: "assets", hashLength: 8 });} catch (error) { if (error instanceof VitePluginError && error.code === VitePluginErrorCode.assetRootMissing) { // create the directory, or point `assetRoot` somewhere else }}Extends#
Error
Constructors#
Constructor#
new VitePluginError(
code,message,options?):VitePluginError
Creates a plugin error.
Parameters#
code#
The stable IGX-#### code for the failure.
message#
string
An actionable description of what went wrong and how to fix it.
options?#
ErrorOptions
Standard Error options; use cause to keep the original failure.
Returns#
Overrides#
Error.constructor
Properties#
cause?#
optionalcause?:unknown
Inherited from#
Error.cause
code#
readonlycode:VitePluginErrorCode
The stable diagnostic code for this failure.
message#
message:
string
Inherited from#
Error.message
name#
name:
string
Inherited from#
Error.name
stack?#
optionalstack?:string
Inherited from#
Error.stack
stackTraceLimit#
staticstackTraceLimit:number
The Error.stackTraceLimit property specifies the number of stack frames
collected by a stack trace (whether generated by new Error().stack or
Error.captureStackTrace(obj)).
The default value is 10 but may be set to any valid JavaScript number. Changes
will affect any stack trace captured after the value has been changed.
If set to a non-number value, or set to a negative number, stack traces will not capture any frames.
Inherited from#
Error.stackTraceLimit
Methods#
captureStackTrace()#
staticcaptureStackTrace(targetObject,constructorOpt?):void
Creates a .stack property on targetObject, which when accessed returns
a string representing the location in the code at which
Error.captureStackTrace() was called.
const myObject = {};Error.captureStackTrace(myObject);myObject.stack; // Similar to `new Error().stack`The first line of the trace will be prefixed with
${myObject.name}: ${myObject.message}.
The optional constructorOpt argument accepts a function. If given, all frames
above constructorOpt, including constructorOpt, will be omitted from the
generated stack trace.
The constructorOpt argument is useful for hiding implementation
details of error generation from the user. For instance:
function a() { b();}function b() { c();}function c() { // Create an error without stack trace to avoid calculating the stack trace twice. const { stackTraceLimit } = Error; Error.stackTraceLimit = 0; const error = new Error(); Error.stackTraceLimit = stackTraceLimit; // Capture the stack trace above function b Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace throw error;}a();Parameters#
targetObject#
object
constructorOpt?#
Function
Returns#
void
Inherited from#
Error.captureStackTrace
prepareStackTrace()#
staticprepareStackTrace(err,stackTraces):any
Parameters#
err#
Error
stackTraces#
CallSite[]
Returns#
any
See#
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
Inherited from#
Error.prepareStackTrace
Interfaces#
AssetChangedPayload#
The payload of an ASSET_CHANGED_EVENT message.
Properties#
address#
readonlyaddress:string
The address of the asset, relative to the asset root.
kind#
readonlykind:"added"|"changed"|"removed"
What happened to it on disk.
url#
readonlyurl:string
The development URL the asset is served from; still meaningful for "removed".
AssetManifest#
The generated assets.manifest.json.
Properties#
entries#
readonlyentries: readonlyAssetManifestEntry[]
Every asset, sorted by address so two builds of the same tree produce the same bytes.
format#
readonlyformat:"ignifx.manifest"
Always ASSET_MANIFEST_FORMAT.
formatVersion#
readonlyformatVersion:1
Always ASSET_MANIFEST_FORMAT_VERSION before 1.0.
root#
readonlyroot:string
The asset root the addresses are relative to, as configured, /-separated.
AssetManifestEntry#
One asset in the manifest.
Properties#
address#
readonlyaddress:string
The address, a /-separated path relative to the asset root.
bytes#
readonlybytes:number
The asset's size in bytes, used for bytes-weighted load progress.
groups#
readonlygroups: readonlystring[]
Group labels from the .meta.json sidecar; [] when the asset declares none.
hash#
readonlyhash:string
The truncated lowercase hex sha256 of the file's contents.
meta?#
readonlyoptionalmeta?:JsonObject
The rest of the sidecar, passed through untouched for the loader that understands it.
type#
readonlytype:AssetType
The asset type, which decides the loader (docs/architecture/05-assets-and-loading.md §5).
url#
readonlyurl:string
Where the asset is fetched from: root-relative in development, content-hashed in a build.
ExtensionPublicAsset#
One file an extension asked to have served as a static asset.
Properties#
fileName#
readonlyfileName:string
The name the file is copied under, which is the name its loader will ask for.
filePath#
readonlyfilePath:string
The absolute path of the file inside the installed package.
packageName#
readonlypackageName:string
The npm name of the package that declared the file.
FormatHeader#
The format header of a file that declares one.
Properties#
format#
readonlyformat:string
The format field, for example "ignifx.scene".
formatVersion#
readonlyformatVersion:number
The formatVersion field, a positive integer.
IgnifxPluginApi#
The plugin's own API object, reachable through Vite's plugin.api.
Remarks#
Watcher callbacks are synchronous, so the rescan a file change triggers runs detached. Anything that needs to observe the result — a test, or another plugin that reads the served manifest — waits for it here instead of guessing at a delay.
Methods#
whenIdle()#
whenIdle():
Promise<void>
Waits until the plugin has finished reacting to every file-system event seen so far.
Returns#
Promise<void>
A promise that settles once the queue of watcher work is empty.
IgnifxPluginOptions#
Options for ignifx.
Properties#
assetRoot?#
readonlyoptionalassetRoot?:string
The directory scanned for assets, relative to the Vite root (or absolute).
Default Value#
"assets"
config?#
readonlyoptionalconfig?:string|false
Path to the project config injected as import.meta.env.IGNIFX_CONFIG
(docs/architecture/04-extensions.md §5). Pass false to inject {} and load nothing.
Default Value#
auto-detected at the Vite root: ignifx.config.ts, .mts, .js, then .mjs.
hashLength?#
readonlyoptionalhashLength?:number
How many hex characters of each asset's sha256 to keep, in the manifest and in hashed file names. Must be between 4 and 64.
Default Value#
8
manifestFileName?#
readonlyoptionalmanifestFileName?:string
The manifest's file name inside build.outDir. It is also the path the dev server serves the
development manifest from.
Default Value#
"assets.manifest.json"
publicPath?#
readonlyoptionalpublicPath?:string
The directory inside build.outDir that hashed assets are written to, and the URL prefix they
are served under in a build. A trailing / is added when missing.
Default Value#
"assets/"
schemas?#
readonlyoptionalschemas?:JsonSchemaProvider
JSON Schemas to validate against, keyed by file format. Without them only the
format/formatVersion header is checked.
Default Value#
no schemas — header validation only.scripts?#
readonlyoptionalscripts?:string
The glob, relative to the Vite root, that virtual:ignifx/scripts collects component and
script classes from.
Default Value#
validate?#
readonlyoptionalvalidate?:boolean
Whether format-headed JSON under the asset root is validated. A failure fails the build.
Default Value#
true
JsonSchemaProvider#
The JSON Schemas the plugin validates against.
Remarks#
The plugin cannot generate these itself: they are derived from the component schemas a game
registers, which only @ignifx/core knows. A game passes them in from its vite.config.ts
(ignifx({ schemas: { scene: sceneFileJsonSchema } })); when nothing is supplied, only the
format/formatVersion header is checked.
Properties#
formats?#
readonlyoptionalformats?:Readonly<Record<string,JsonObject>>
Schemas keyed by the file's format header, for every other format.
scene?#
readonlyoptionalscene?:JsonObject
The schema for ignifx.scene files — *.scene.json and *.prefab.json alike (ADR-0005).
ResolvedIgnifxConfig#
The outcome of resolving the project config.
Properties#
config#
readonlyconfig:JsonObject
The config object, always JSON-safe; {} when no config exists.
dependencies#
readonlydependencies: readonlystring[]
Files the loader read, so the dev server can watch them for a full reload.
path#
readonlypath:string|null
The absolute path of the file that was loaded, or null when no config exists.
ResolvedIgnifxPluginOptions#
IgnifxPluginOptions with every default applied and every value normalized.
Properties#
assetRoot#
readonlyassetRoot:string
The asset root exactly as configured; the plugin resolves it against the Vite root later.
configFile#
readonlyconfigFile:string|false|null
The configured config path, null to auto-detect, or false to inject {}.
hashLength#
readonlyhashLength:number
The truncated hash length.
manifestFileName#
readonlymanifestFileName:string
The manifest file name, without a leading /.
publicPath#
readonlypublicPath:string
The public path, without a leading / and with a trailing /.
schemas#
readonlyschemas:JsonSchemaProvider|null
The supplied schemas, or null.
scriptsPattern#
readonlyscriptsPattern:string
The scripts glob, rewritten to be root-absolute.
validate#
readonlyvalidate:boolean
Whether validation runs.
ScanAssetRootOptions#
Options for scanAssetRoot.
Properties#
assetRoot#
readonlyassetRoot:string
Absolute path of the directory to scan.
hashLength?#
readonlyoptionalhashLength?:number
How many hex characters of the sha256 to keep.
Default Value#
8
ScannedAsset#
An asset found on disk, before a URL has been decided for it.
Properties#
address#
readonlyaddress:string
The address, a /-separated path relative to the asset root.
bytes#
readonlybytes:number
The file's size in bytes.
filePath#
readonlyfilePath:string
The absolute path of the file on disk.
groups#
readonlygroups: readonlystring[]
Group labels from the sidecar, or [].
hash#
readonlyhash:string
The truncated lowercase hex sha256 of the file's contents.
meta#
readonlymeta:JsonObject|null
The sidecar minus its groups key, or null when there is no sidecar or nothing is left.
type#
readonlytype:AssetType
The asset type derived from the address.
SchemaViolation#
One failed constraint, addressed by a JSON pointer into the validated document.
Properties#
message#
readonlymessage:string
What the value is, and what the schema required instead.
pointer#
readonlypointer:string
RFC 6901 pointer to the offending value; "" for the document root.
ScriptsModuleOptions#
Options accepted by scriptsModuleSource.
Properties#
hot?#
readonlyoptionalhot?:boolean
Emit the HMR client. false — the default, and what a build uses — leaves the module with no
import.meta.hot reference at all, so none of the client reaches production
(docs/architecture/15-devtools-and-diagnostics.md §5).
ValidateJsonOptions#
Options for validateJsonValue.
Properties#
maxDepth?#
readonlyoptionalmaxDepth?:number
How deep the validator may recurse before it gives up on a recursive schema.
Default Value#
64
root?#
readonlyoptionalroot?:JsonObject
The document $ref targets are resolved against.
Default Value#
the schema argument itself, which is what a self-contained schema wants.
ValidationProblem#
One validation failure, addressed by asset and JSON pointer.
Properties#
address#
readonlyaddress:string
The address of the offending asset.
code#
readonlycode:VitePluginErrorCode
The diagnostic code: IGX-0650, IGX-0651, or IGX-0652.
filePath#
readonlyfilePath:string
The absolute path of the offending file, for editors that jump to it.
message#
readonlymessage:string
What is wrong, in one sentence.
pointer#
readonlypointer:string
RFC 6901 pointer to the offending value; "" for the document as a whole.
Type Aliases#
AssetType#
AssetType = typeof
ASSET_TYPE_BY_SUFFIX[keyof typeofASSET_TYPE_BY_SUFFIX] | typeofASSET_TYPE_BY_EXTENSION[keyof typeofASSET_TYPE_BY_EXTENSION]
The asset type recorded in a manifest entry. Derived from the two tables so that adding a row
widens the union automatically (coding standards §5.2 prefers as const tables over enums).
JsonArray#
JsonArray = readonly
JsonValue[]
A JSON array.
JsonObject#
JsonObject =
object
A JSON object.
Index Signature#
[key: string]: JsonValue
JsonSchemaObject#
JsonSchemaObject =
JsonObject
A JSON Schema fragment, kept as a plain JSON object exactly as @ignifx/core emits it.
JsonValue#
JsonValue =
string|number|boolean|null|JsonArray|JsonObject
A JSON value.
VitePluginErrorCode#
VitePluginErrorCode = typeof
VitePluginErrorCode[keyof typeofVitePluginErrorCode]
The union of the diagnostic codes this package can report.
Variables#
ASSET_CHANGED_EVENT#
constASSET_CHANGED_EVENT:"ignifx:asset-changed"="ignifx:asset-changed"
The HMR event the plugin sends when an asset under the asset root changes.
Remarks#
The assets service listens for it and reloads the affected handles, firing
AssetHandle.onReplaced (docs/architecture/05-assets-and-loading.md §7). The policy of a
script hot reload — patch or recreate — is Phase 10's; this package only ships the channel.
Example#
import.meta.hot?.on("ignifx:asset-changed", (payload) => { console.log(payload.address, payload.kind);});ASSET_MANIFEST_FORMAT#
constASSET_MANIFEST_FORMAT:"ignifx.manifest"="ignifx.manifest"
The format header every generated manifest carries
(docs/architecture/06-serialization-and-scene-format.md §6).
ASSET_MANIFEST_FORMAT_VERSION#
constASSET_MANIFEST_FORMAT_VERSION:1=1
The manifest format version. It stays 1 for the whole 0.x line (CONSTITUTION.md §4.2).
ASSET_TYPE_BY_EXTENSION#
constASSET_TYPE_BY_EXTENSION:object
Asset types keyed by file extension, from the core loader table of
docs/architecture/05-assets-and-loading.md §5. audio is listed here even though
@ignifx/audio registers the loader: the manifest is written once, by this plugin, for every
extension that will later read it.
Type Declaration#
.basis#
readonly.basis:"texture"="texture"
.bin#
readonly.bin:"binary"="binary"
.csv#
readonly.csv:"text"="text"
.dds#
readonly.dds:"environment"="environment"
.env#
readonly.env:"environment"="environment"
.glb#
readonly.glb:"model"="model"
.gltf#
readonly.gltf:"model"="model"
.hdr#
readonly.hdr:"environment"="environment"
.jpeg#
readonly.jpeg:"texture"="texture"
.jpg#
readonly.jpg:"texture"="texture"
.json#
readonly.json:"json"="json"
.ktx2#
readonly.ktx2:"texture"="texture"
.md#
readonly.md:"text"="text"
.mp3#
readonly.mp3:"audio"="audio"
.ogg#
readonly.ogg:"audio"="audio"
.otf#
readonly.otf:"font"="font"
.png#
readonly.png:"texture"="texture"
.ttf#
readonly.ttf:"font"="font"
.txt#
readonly.txt:"text"="text"
.wasm#
readonly.wasm:"binary"="binary"
.wav#
readonly.wav:"audio"="audio"
.webp#
readonly.webp:"texture"="texture"
ASSET_TYPE_BY_SUFFIX#
constASSET_TYPE_BY_SUFFIX:object
Asset types keyed by the two-segment JSON extension that identifies them. These are checked
before ASSET_TYPE_BY_EXTENSION because every one of them also ends in .json
(docs/architecture/06-serialization-and-scene-format.md §6).
Type Declaration#
.animator.json#
readonly.animator.json:"animator"="animator"
.atlas.json#
readonly.atlas.json:"spriteatlas"="spriteatlas"
.audio.json#
readonly.audio.json:"audiobuses"="audiobuses"
.i18n.json#
readonly.i18n.json:"i18n"="i18n"
.input.json#
readonly.input.json:"inputactions"="inputactions"
.material.json#
readonly.material.json:"material"="material"
.physicsmaterial.json#
readonly.physicsmaterial.json:"physicsmaterial"="physicsmaterial"
.prefab.json#
readonly.prefab.json:"scene"="scene"
.scene.json#
readonly.scene.json:"scene"="scene"
.spriteanim.json#
readonly.spriteanim.json:"spriteanimation"="spriteanimation"
.tilemap.json#
readonly.tilemap.json:"tilemap"="tilemap"
DEFAULT_ASSET_ROOT#
constDEFAULT_ASSET_ROOT:"assets"="assets"
The asset root, relative to the Vite root, when the option is not given.
DEFAULT_ASSET_TYPE#
constDEFAULT_ASSET_TYPE:"binary"="binary"
The type given to a file whose extension is in neither table. Bytes are always loadable, so an
unknown extension is a binary asset rather than a build failure.
DEFAULT_HASH_LENGTH#
constDEFAULT_HASH_LENGTH:8=8
The default number of hex characters kept from a content hash, matching the length Vite itself uses for asset file names.
DEFAULT_MANIFEST_FILE_NAME#
constDEFAULT_MANIFEST_FILE_NAME:"assets.manifest.json"="assets.manifest.json"
The manifest's file name inside build.outDir, when the option is not given.
DEFAULT_PUBLIC_PATH#
constDEFAULT_PUBLIC_PATH:"assets/"="assets/"
The directory inside build.outDir that assets are copied into, when the option is not given.
DEFAULT_SCRIPTS_PATTERN#
constDEFAULT_SCRIPTS_PATTERN:"src/scripts/**/*.ts"="src/scripts/**/*.ts"
The default glob the script registry is built from, relative to the Vite root.
EXTENSION_KEYWORD#
constEXTENSION_KEYWORD:"ignifx-extension"="ignifx-extension"
The keyword a package outside the @ignifx scope sets to opt into extension discovery
(docs/architecture/04-extensions.md §6).
IGNIFX_CONFIG_DEFINE_KEY#
constIGNIFX_CONFIG_DEFINE_KEY:"import.meta.env.IGNIFX_CONFIG"="import.meta.env.IGNIFX_CONFIG"
The define key the resolved project config is injected under
(docs/architecture/04-extensions.md §5).
IGNIFX_CONFIG_FILE_NAMES#
constIGNIFX_CONFIG_FILE_NAMES: readonly ["ignifx.config.ts","ignifx.config.mts","ignifx.config.js","ignifx.config.mjs"]
The file names auto-detected at the Vite root, in the order they are tried.
MANIFEST_MODULE_ID#
constMANIFEST_MODULE_ID:"virtual:ignifx/manifest"="virtual:ignifx/manifest"
The module a game imports to read the asset manifest without fetching it.
Example#
import { manifest } from "virtual:ignifx/manifest";PLUGIN_NAME#
constPLUGIN_NAME:"ignifx"="ignifx"
The plugin's name, as it appears in Vite logs and in PLUGIN_ERROR diagnostics.
RESOLVED_MANIFEST_MODULE_ID#
constRESOLVED_MANIFEST_MODULE_ID:string
The resolved id of MANIFEST_MODULE_ID.
RESOLVED_SCRIPTS_MODULE_ID#
constRESOLVED_SCRIPTS_MODULE_ID:string
The resolved id of SCRIPTS_MODULE_ID.
SCRIPTS_HOT_RELOAD_EXPORT#
constSCRIPTS_HOT_RELOAD_EXPORT:"acceptHotReload"="acceptHotReload"
The function virtual:ignifx/scripts exports for wiring an app to script hot reload
(docs/architecture/15-devtools-and-diagnostics.md §5).
Remarks#
The generated module cannot know which app — or how many apps — a page built, and a module-level
app reference is exactly what CONSTITUTION.md §3.5/§3.6 forbid. So the hand-off is explicit and
one line of game code: the game passes its app in, and the module calls app.hotReload.apply
whenever Vite replaces a script module. The export exists in a production build too, with an
empty body, so the same source builds either way.
Example#
import { acceptHotReload, scripts } from "virtual:ignifx/scripts";const app = await createApp({ canvas });app.registerComponents(scripts);acceptHotReload(app);SCRIPTS_MODULE_ID#
constSCRIPTS_MODULE_ID:"virtual:ignifx/scripts"="virtual:ignifx/scripts"
The module a game imports to get every script and component class in its source tree
(docs/architecture/15-devtools-and-diagnostics.md §5).
Example#
import { scripts } from "virtual:ignifx/scripts";const app = await createApp({ canvas, components: scripts });VitePluginErrorCode#
constVitePluginErrorCode:object
The diagnostic codes this package can report, in the 05xx (assets) and 06xx (serialization)
ranges reserved by docs/architecture/15-devtools-and-diagnostics.md §1.
Type Declaration#
assetRootMissing#
readonlyassetRootMissing:"IGX-0550"="IGX-0550"
The configured asset root does not exist or is not a directory.
duplicateOutputFile#
readonlyduplicateOutputFile:"IGX-0552"="IGX-0552"
Two assets hash to the same output file name, so one would overwrite the other.
extensionAssetMissing#
readonlyextensionAssetMissing:"IGX-0553"="IGX-0553"
An ignifx.assets.public entry of an extension package could not be read.
invalidOption#
readonlyinvalidOption:"IGX-0555"="IGX-0555"
A plugin option is outside its documented domain.
invalidProjectConfig#
readonlyinvalidProjectConfig:"IGX-0554"="IGX-0554"
ignifx.config.ts could not be loaded, or its default export is not an object.
invalidSidecar#
readonlyinvalidSidecar:"IGX-0551"="IGX-0551"
A .meta.json sidecar could not be parsed, or its groups field is not an array of strings.
malformedJson#
readonlymalformedJson:"IGX-0650"="IGX-0650"
A JSON asset under the asset root is not parseable JSON.
missingFormatHeader#
readonlymissingFormatHeader:"IGX-0651"="IGX-0651"
A format-headed JSON file is missing its format/formatVersion header or the header is malformed.
schemaViolation#
readonlyschemaViolation:"IGX-0652"="IGX-0652"
A format-headed JSON file failed validation against the JSON Schema supplied for its format.
unsupportedSchema#
readonlyunsupportedSchema:"IGX-0653"="IGX-0653"
A supplied JSON Schema uses a keyword or $ref target this validator does not implement.
Functions#
addressFromRelativePath()#
addressFromRelativePath(
relativePath):string
Converts an OS path relative to the asset root into an address.
Parameters#
relativePath#
string
The path relative to the asset root, in platform separators.
Returns#
string
The /-separated address.
appendPointer()#
appendPointer(
pointer,segment):string
Appends a segment to a JSON pointer.
Parameters#
pointer#
string
The pointer to the parent value.
segment#
string
The property name or array index to append.
Returns#
string
The pointer to the child value.
Example#
appendPointer("", "entities"); // "/entities"appendPointer("/entities", "0"); // "/entities/0"assetTypeForAddress()#
assetTypeForAddress(
address):AssetType
Classifies an asset address by its extension.
Parameters#
address#
string
The asset address, a /-separated path relative to the asset root.
Returns#
The asset type, or DEFAULT_ASSET_TYPE when the extension is unknown.
Example#
assetTypeForAddress("levels/level1.scene.json"); // "scene"assetTypeForAddress("sprites/hero.png"); // "texture"assetTypeForAddress("data/loot.json"); // "json"buildManifest()#
buildManifest(
assets,root,urlFor):AssetManifest
Turns scanned assets into a manifest.
Parameters#
assets#
readonly ScannedAsset[]
The assets to list, already sorted by address.
root#
string
The asset root as configured, recorded in the manifest for diagnostics.
urlFor#
(asset) => string
Maps an asset to the URL the runtime fetches it from.
Returns#
The manifest, ready to be serialized.
Example#
const manifest = buildManifest(assets, "assets", (asset) => `/assets/${asset.address}`);collectExtensionPublicAssets()#
collectExtensionPublicAssets(
root):Promise<readonlyExtensionPublicAsset[]>
Collects every file declared by an installed extension's ignifx.assets.public.
Parameters#
root#
string
The absolute project root to resolve packages from.
Returns#
Promise<readonly ExtensionPublicAsset[]>
The files to copy, sorted by package name and then file name.
Remarks#
Packages in the @ignifx scope are always inspected; any other package must carry the
EXTENSION_KEYWORD keyword. node_modules directories are walked from the project root
upwards, so a pnpm workspace finds hoisted packages, and the nearest installation of a package
wins.
Throws#
A VitePluginError with code IGX-0553 when a declared file does not exist, or
when two packages declare files with the same name, which would silently overwrite one.
Example#
const files = await collectExtensionPublicAssets("/project");// [{ packageName: "@ignifx/physics", fileName: "HavokPhysics.wasm", filePath: "…" }]defineConfig()#
defineConfig<
T>(config):T
Identity helper that gives a game's ignifx.config.ts its types without widening the object.
Type Parameters#
T#
T
The shape of the project config, inferred from the argument.
Parameters#
config#
T
The project config object.
Returns#
T
The same object, unchanged.
Remarks#
ignifx/config re-exports this in Phase 12 so that a game can write
import { defineConfig } from "ignifx/config" rather than reaching into the build plugin. The
function is deliberately generic and lossless: it returns the argument, so literal types such as
the layer-name tuple survive into the game's own typings.
Example#
// ignifx.config.tsimport { defineConfig } from "@ignifx/vite-plugin";export default defineConfig({ layers: ["Default", "Ground", "Player"], time: { fixedDeltaTime: 1 / 60 }, assets: { root: "./assets", preload: ["boot"] },});findIgnifxConfigFile()#
findIgnifxConfigFile(
root):Promise<string|null>
Finds the project config file at a Vite root.
Parameters#
root#
string
The absolute Vite root directory.
Returns#
Promise<string | null>
The absolute path of the first name in IGNIFX_CONFIG_FILE_NAMES that exists, or
null when the project has no config file.
formatValidationProblem()#
formatValidationProblem(
problem):string
Renders a problem as a single log line: <address> <pointer>: <message> (<code>).
Parameters#
problem#
The problem to render.
Returns#
string
The line, without a trailing newline.
Example#
formatValidationProblem({ address: "levels/level1.scene.json", filePath: "/p/assets/levels/level1.scene.json", pointer: "/entities/0", message: 'missing required property "uid"', code: "IGX-0652",});// 'levels/level1.scene.json /entities/0: missing required property "uid" (IGX-0652)'hashedAddress()#
hashedAddress(
address,hash):string
Builds the content-hashed output name for an address.
Parameters#
address#
string
The asset address, a /-separated path relative to the asset root.
hash#
string
The truncated content hash to insert.
Returns#
string
The address with the hash inserted before the extension.
Example#
hashedAddress("levels/level1.scene.json", "0a1b2c3d"); // "levels/level1.0a1b2c3d.scene.json"ignifx()#
ignifx(
options?):Plugin<IgnifxPluginApi>
The ignifx Vite plugin.
Parameters#
options?#
IgnifxPluginOptions = {}
Plugin options and their documented defaults; see IgnifxPluginOptions.
Returns#
Plugin<IgnifxPluginApi>
The Vite plugin, to be listed in vite.config.ts.
Remarks#
What it does, in the order the hooks run:
configresolvesignifx.config.tsand injects it asimport.meta.env.IGNIFX_CONFIG(docs/architecture/04-extensions.md§5).buildStartscans the asset root, hashes every file, reads.meta.jsonsidecars, and validates every format-headed JSON file. A validation failure fails the build.virtual:ignifx/manifestandvirtual:ignifx/scriptsare served fromresolveId/load.- In development the manifest is also served from
/<manifestFileName>, the asset root is watched, and every change is announced on theignifx:asset-changedHMR channel; editing the project config triggers a full reload. generateBundleemits every asset under<outDir>/<publicPath>with a content-hashed, immutable-cacheable name, copies each extension'signifx.assets.publicfiles unhashed next to them, and writes the manifest with the hashed URLs.
Throws#
A VitePluginError with code IGX-0555 when an option is outside its domain.
Example#
// vite.config.tsimport { defineConfig } from "vite";import { ignifx } from "@ignifx/vite-plugin";export default defineConfig({ plugins: [ignifx({ assetRoot: "assets" })],});isJsonArray()#
isJsonArray(
value):value is JsonArray
Reports whether a JSON value is an array.
Parameters#
value#
The value to test.
Returns#
value is JsonArray
true when the value is a JSON array, narrowed with its element type intact.
isJsonObject()#
isJsonObject(
value):value is JsonObject
Reports whether a JSON value is an object rather than an array, null, or a primitive.
Parameters#
value#
The value to test.
Returns#
value is JsonObject
true when the value is a JSON object.
Remarks#
Array.isArray alone does not narrow a JsonValue: JsonArray is a readonly array, which the
compiler cannot subtract in the false branch, and its true branch widens to any[]. This
predicate and isJsonArray are the one place that gap is papered over, so every other
module narrows in a single call and keeps its element types.
Example#
isJsonObject({ a: 1 }); // trueisJsonObject([1, 2]); // falseisSidecarFileName()#
isSidecarFileName(
fileName):boolean
Reports whether a file name is an asset sidecar rather than an asset.
Parameters#
fileName#
string
The base name of the file.
Returns#
boolean
true for *.meta.json, which the scanner reads but never lists as an entry.
jsonProperty()#
jsonProperty(
object,key):JsonValue|undefined
Reads a property of a JSON object without tripping noPropertyAccessFromIndexSignature, and
without ever reaching the prototype chain.
Parameters#
object#
The object to read.
key#
string
The property name.
Returns#
JsonValue | undefined
The value, or undefined when the object has no such own property.
loadIgnifxConfig()#
loadIgnifxConfig(
configFile,root,env):Promise<ResolvedIgnifxConfig>
Loads ignifx.config.ts through Vite's own config loader.
Parameters#
configFile#
string | null
Absolute path of the config file, or null to resolve to an empty config.
root#
string
The absolute Vite root, used as the loader's config root.
env#
ConfigEnv
The Vite command and mode the config is being loaded for.
Returns#
Promise<ResolvedIgnifxConfig>
The resolved config, its path, and the files the loader depended on.
Remarks#
Vite's loadConfigFromFile is used rather than a bare dynamic import because the config is
TypeScript: the loader bundles it with the same pipeline that loads vite.config.ts, so the game
needs no separate build step and the file may import helpers from its own source tree. The loader
types its result as a Vite UserConfig; it is in fact whatever the module exports by default,
which is the boundary assertion below (coding standards §5.2).
Throws#
A VitePluginError with code IGX-0554 when the file cannot be loaded or does not
export a JSON object by default.
Example#
const resolved = await loadIgnifxConfig("/project/ignifx.config.ts", "/project", { command: "build", mode: "production",});resolved.config; // { layers: [...], time: {...} }manifestModuleSource()#
manifestModuleSource(
manifest):string
Generates the source of virtual:ignifx/manifest.
Parameters#
manifest#
The manifest to embed.
Returns#
string
An ES module exporting the manifest as manifest.
normalizeScriptsPattern()#
normalizeScriptsPattern(
pattern):string
Turns a scripts glob into the root-relative form import.meta.glob needs.
Parameters#
pattern#
string
The pattern as configured, relative to the Vite root or already root-absolute.
Returns#
string
The root-absolute pattern.
Remarks#
import.meta.glob resolves a relative pattern against the importing file, and a virtual module
has no directory to be relative to, so the pattern is rewritten to start at the Vite root. Vite
documents a leading / as exactly that.
Throws#
A VitePluginError with code IGX-0555 when the pattern is empty or escapes the
root with ...
Example#
normalizeScriptsPattern("src/scripts/player.ts"); // "/src/scripts/player.ts"parseJsonValue()#
parseJsonValue(
text):JsonValue
Parses JSON text into the JSON value model.
Parameters#
text#
string
The JSON text.
Returns#
The parsed value.
Remarks#
This is the one place in the package where JSON.parse's any is narrowed. Every caller works
in JsonValue from here on, which is what lets the rest of the code check shapes instead of
asserting them (coding standards §5.2).
Throws#
A SyntaxError when the text is not JSON; callers turn that into a diagnostic.
requiresFormatHeader()#
requiresFormatHeader(
address):boolean
Reports whether a JSON file must carry a format/formatVersion header.
Parameters#
address#
string
The asset address.
Returns#
boolean
true when a missing header is an error.
Remarks#
Every ignifx file format is a two-segment JSON extension — .scene.json, .prefab.json,
.material.json, .atlas.json, .input.json, and the rest of the table in
docs/architecture/06-serialization-and-scene-format.md §6 — so a two-segment name is the signal
that a header is expected. A plain .json file is game data and is validated only when it
happens to declare a header of its own.
resolvePluginOptions()#
resolvePluginOptions(
options):ResolvedIgnifxPluginOptions
Applies the documented defaults and rejects options outside their domain.
Parameters#
options#
The options as given to ignifx.
Returns#
The resolved options.
Throws#
A VitePluginError with code IGX-0555 when an option is outside its domain.
Example#
resolvePluginOptions({}).publicPath; // "assets/"resolvePluginOptions({ publicPath: "/static" }).publicPath; // "static/"resolveVirtualModuleId()#
resolveVirtualModuleId(
id):string|null
Maps a bare virtual specifier to its resolved id.
Parameters#
id#
string
The specifier as written in the importing module.
Returns#
string | null
The resolved id, or null when the specifier belongs to another plugin.
scanAssetRoot()#
scanAssetRoot(
options):Promise<readonlyScannedAsset[]>
Scans an asset root and describes every file it holds.
Parameters#
options#
The directory to scan and the hash length; see ScanAssetRootOptions.
Returns#
Promise<readonly ScannedAsset[]>
Every asset found, sorted by address.
Remarks#
Dot-prefixed files and directories are skipped, and so are *.meta.json sidecars — they are read
for the asset they belong to instead of listed as assets of their own.
Throws#
A VitePluginError with code IGX-0550 when the asset root does not exist or is
not a directory, or IGX-0551 when a sidecar is malformed.
Example#
const assets = await scanAssetRoot({ assetRoot: "/project/assets" });// [{ address: "sprites/hero.png", type: "texture", hash: "0a1b2c3d", … }]scriptsModuleSource()#
scriptsModuleSource(
pattern,options?):string
Generates the source of virtual:ignifx/scripts.
Parameters#
pattern#
string
The root-absolute glob, from normalizeScriptsPattern.
options?#
Whether to emit the development-only HMR client.
Returns#
string
An ES module exporting the registry as scripts and the wiring as acceptHotReload.
Remarks#
The registry keeps every exported class that declares a typeId, which is what makes a class a
registrable component (docs/architecture/03-scripting-and-components.md). Plain helper exports
from the same files are ignored, and the module keys are sorted so that the registration order is
the same on every machine. Because the imports are real static imports produced by
import.meta.glob's eager form, Vite's HMR graph sees each script file and can push updates for
it.
The HMR half. Script hot reload needs no channel of its own: unlike an asset, a script module
already sits in Vite's module graph, so the generated module self-accepts and Vite hands it the
replacement namespace. The handler diffs the old and new registries by typeId for the log line
and hands the whole new registry to app.hotReload.apply, which is the half that decides what a
change means — patch or recreate — and skips the classes that did not change. The set of
subscribed apps lives in import.meta.hot.data, which Vite carries from one instance of a module
to the next, so the module that handles the second update still knows about them.
serializeManifest()#
serializeManifest(
manifest):string
Serializes a manifest the way the plugin writes it: two-space indentation and a trailing newline, so that a committed or inspected manifest reads as an ordinary JSON file.
Parameters#
manifest#
The manifest to serialize.
Returns#
string
The manifest as JSON text.
splitAssetFileName()#
splitAssetFileName(
fileName):object
Splits an asset file name into the part a content hash is inserted after and the extension that must survive it.
Parameters#
fileName#
string
The base name of the file, without a directory.
Returns#
object
The stem and the extension, whose concatenation is fileName.
extension#
readonlyextension:string
stem#
readonlystem:string
Remarks#
Every compound ignifx format is a two-segment JSON extension (.scene.json, .atlas.json,
.input.json, …), and the runtime picks its loader from that whole suffix, so the hash has to go
in front of it: level1.scene.json becomes level1.<hash>.scene.json, not
level1.scene.<hash>.json. Every other name keeps the ordinary single extension.
Example#
splitAssetFileName("level1.scene.json"); // { stem: "level1", extension: ".scene.json" }splitAssetFileName("hero.glb"); // { stem: "hero", extension: ".glb" }validateJsonAsset()#
validateJsonAsset(
address,filePath,text,schemas): readonlyValidationProblem[]
Validates one JSON document that lives under the asset root.
Parameters#
address#
string
The asset address, used in messages.
filePath#
string
The absolute path of the file.
text#
string
The file's contents.
schemas#
JsonSchemaProvider | null
The schemas the game supplied, or null to check only the header.
Returns#
readonly ValidationProblem[]
Every problem found, in document order.
Example#
const problems = validateJsonAsset("a.scene.json", "/p/assets/a.scene.json", "{}", null);// [{ code: "IGX-0651", message: 'is missing its "format"/"formatVersion" header', … }]validateJsonAssets()#
validateJsonAssets(
assets,schemas):Promise<readonlyValidationProblem[]>
Validates every JSON asset in a scan.
Parameters#
assets#
readonly ScannedAsset[]
The scanned assets to check; anything that is not JSON is skipped.
schemas#
JsonSchemaProvider | null
The schemas the game supplied, or null to check only headers.
Returns#
Promise<readonly ValidationProblem[]>
Every problem found, ordered by address.
Throws#
A VitePluginError with code IGX-0653 when a supplied schema uses a keyword
outside the implemented subset.
validateJsonValue()#
validateJsonValue(
value,schema,options?): readonlySchemaViolation[]
Validates a JSON document against the supported JSON Schema subset: type, required,
properties, additionalProperties, items, enum, const, minimum, maximum, minItems,
maxItems, oneOf, anyOf, and $ref to #/$defs/*.
Parameters#
value#
The parsed document to validate.
schema#
The schema to validate against.
options?#
Reference root and recursion limit; see ValidateJsonOptions.
Returns#
readonly SchemaViolation[]
Every violation found, in document order; an empty array means the document is valid.
Throws#
A VitePluginError with code IGX-0653 when the schema uses a keyword outside the
subset, references something other than #/$defs/*, or recurses without bound.
Example#
const problems = validateJsonValue({ format: "ignifx.scene" }, { type: "object", required: ["format", "formatVersion"], properties: { format: { const: "ignifx.scene" }, formatVersion: { type: "integer" } },});// [{ pointer: "", message: 'missing required property "formatVersion"' }]