ignifx0.x · unpublished
GitHub

API reference·skills/ignifx/references/api/cli.md

@ignifx/cli

@ignifx/cli public barrel. Explicit named re-exports only — no export * (coding standards §4).

Remarks#

The create-ignifx executable is src/bin.ts; it is not part of this barrel because it is a process entry point, not a library API.

Classes#

CliError#

The error type every @ignifx/cli API throws for misuse (CONSTITUTION.md §3.9). It always carries a stable IGX-#### code so the process boundary can report a machine-readable failure.

Example#

typescript
try {  await copyTemplate({ templateDir, targetDir });} catch (error) {  if (error instanceof CliError && error.code === "IGX-1401") {    console.error("pass --overwrite to write into a non-empty directory");  }}

Extends#

  • Error

Constructors#

Constructor#

new CliError(code, message, options?): CliError

Creates a CLI error.

Parameters#
code#

CliErrorCode

The stable IGX-14## 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#

CliError

Overrides#

Error.constructor

Properties#

cause?#

optional cause?: unknown

Inherited from#

Error.cause

code#

readonly code: CliErrorCode

The stable diagnostic code for this failure.

message#

message: string

Inherited from#

Error.message

name#

name: string

Inherited from#

Error.name

stack?#

optional stack?: string

Inherited from#

Error.stack

stackTraceLimit#

static stackTraceLimit: 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()#

static captureStackTrace(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.

javascript
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:

javascript
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()#

static prepareStackTrace(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#

CopyTemplateOptions#

Options for copyTemplate.

Properties#

dependencyRange?#

readonly optional dependencyRange?: string | null

The range that replaces a workspace: dependency specifier in the copied package.json.

Remarks#

workspace:* and workspace:^ become ^<range>; workspace:~ becomes ~<range>; any other workspace:<something> keeps whatever followed the colon, which is what pnpm itself does when it publishes. Pass null to copy package.json byte for byte.

Default Value#

DEFAULT_DEPENDENCY_RANGE

desktop?#

readonly optional desktop?: boolean

Whether the template's desktop variant is copied.

Remarks#

false skips DESKTOP_ONLY_ENTRIES and strips the desktop scripts and dependencies from the copied package.json; true copies the template whole. This is what --desktop sets.

Default Value#

false

ignore?#

readonly optional ignore?: readonly string[]

Entry names that are skipped, together with everything below them.

Default Value#

DEFAULT_IGNORED_ENTRIES

overwrite?#

readonly optional overwrite?: boolean

Whether to write into a target directory that already contains entries.

Default Value#

false — an existing, non-empty target throws IGX-1401 instead.

projectName?#

readonly optional projectName?: string | null

The name written into the copied package.json. Defaults to the target directory's base name, lower-cased, with any run of characters outside a-z0-9._- replaced by -; null keeps the template's own name.

rename?#

readonly optional rename?: Readonly<Record<string, string>>

Map of source file name to the name it is written under, applied to every entry at every depth.

Default Value#

DEFAULT_TEMPLATE_RENAMES

signal?#

readonly optional signal?: AbortSignal

Signal that cancels the copy (coding standards §8). An already-aborted signal rejects with the signal's reason before anything is written.

Default Value#
typescript
no signalthe copy runs to completion.
targetDir#

readonly targetDir: string

Absolute or relative path of the directory to write into; created if it does not exist.

templateDir#

readonly templateDir: string

Absolute or relative path of the template directory to read from.


CopyTemplateResult#

What copyTemplate wrote.

Properties#

files#

readonly files: readonly string[]

Paths of the files written, relative to targetDir, /-separated on every platform and sorted lexicographically. Directories are not listed.


CreateCommand#

A parsed create-ignifx invocation.

Properties#

desktop#

readonly desktop: boolean

Whether --desktop was given, which copies the template's Electron variant (docs/architecture/14-platform-electron.md §3).

overwrite#

readonly overwrite: boolean

Whether --overwrite was given, allowing a non-empty target directory.

targetDir#

readonly targetDir: string

Directory the project is scaffolded into, exactly as the user typed it.

template#

readonly template: string

Template name to copy; DEFAULT_TEMPLATE when --template was not given.


CreateIo#

The side-effecting surface runCreate is allowed to touch.

Remarks#

Everything the command needs from the outside world is injected, so runCreate never reads process and unit tests never spawn one (CONSTITUTION.md §3.6). Only src/bin.ts builds an implementation backed by the real process.

Properties#

stderr#

readonly stderr: (line) => void

Writes one line of diagnostic output. Used by the process boundary to report a failure.

Parameters#
line#

string

The line to write, without a trailing newline.

Returns#

void

stdout#

readonly stdout: (line) => void

Writes one line of progress output.

Parameters#
line#

string

The line to write, without a trailing newline.

Returns#

void

templatesRoot#

readonly templatesRoot: string

Directory that holds one subdirectory per template.

Type Aliases#

CliErrorCode#

CliErrorCode = typeof CliErrorCode[keyof typeof CliErrorCode]

The union of the error codes this package can throw.

Variables#

CliErrorCode#

const CliErrorCode: object

Error codes owned by @ignifx/cli. They live in the 14xx platform range reserved by docs/architecture/15-devtools-and-diagnostics.md §1.

Type Declaration#

invalidArguments#

readonly invalidArguments: "IGX-1403" = "IGX-1403"

The command line could not be parsed, or an argument is not usable.

targetNotEmpty#

readonly targetNotEmpty: "IGX-1401" = "IGX-1401"

The target directory already exists and is not empty, and overwrite was not requested.

templateNotFound#

readonly templateNotFound: "IGX-1402" = "IGX-1402"

The requested template directory does not exist.

Remarks#

The CLI runs before an App exists, so it cannot throw core's IgnifxError. Phase 1 introduces the full IgnifxError registry described in that document; when it lands, these codes are expected to fold into it and CliError becomes a thin alias or is removed.


DEFAULT_DEPENDENCY_RANGE#

const DEFAULT_DEPENDENCY_RANGE: string

The version a scaffolded project's @ignifx/* dependencies are pinned to.

Remarks#

A template inside this repository declares "@ignifx/core": "workspace:*", which is pnpm's workspace protocol: it means "whatever the checkout has" and is meaningless outside a workspace. A generated project has to name a published range instead, and because the whole @ignifx scope ships one version line (docs/architecture/00-overview.md §2) that range is this package's own version.


DEFAULT_IGNORED_ENTRIES#

const DEFAULT_IGNORED_ENTRIES: readonly string[]

Directory and file names copyTemplate skips by default: build and tooling output that a template checkout may contain locally but that must never reach a generated project.


DEFAULT_TEMPLATE#

const DEFAULT_TEMPLATE: "2d-topdown" = "2d-topdown"

The template used when --template is not given.


DEFAULT_TEMPLATE_RENAMES#

const DEFAULT_TEMPLATE_RENAMES: Readonly<Record<string, string>>

File names that copyTemplate rewrites by default.

Remarks#

npm refuses to publish a .gitignore inside a package and rewrites a nested package.json during packing, so template files that must arrive under those names are stored with an underscore prefix and restored on copy. This is the convention used by every scaffolder that ships its templates inside an npm package.


DESKTOP_ONLY_DEPENDENCIES#

const DESKTOP_ONLY_DEPENDENCIES: readonly string[]

The devDependencies a template declares only for its desktop variant, removed from a browser-only scaffold.

Remarks#

@ignifx/electron is deliberately not on this list. A template's src/main.ts registers electron() unconditionally — the extension is inert without a preload bridge, which is what gives one renderer bundle both builds — so removing the package would break the browser scaffold it was meant to slim down. What is removed is the three build tools, which are large binary downloads a browser game never runs.


DESKTOP_ONLY_ENTRIES#

const DESKTOP_ONLY_ENTRIES: readonly string[]

Entries that belong to a template's desktop variant and are skipped unless the scaffold asked for one (docs/architecture/14-platform-electron.md §3).

Remarks#

A browser-only project should not carry them, and not only for tidiness: electron and electron-builder are large binary downloads that a browser game never runs, and an electron.vite.config.ts in a project with no desktop/ directory is a config that names files that are not there.


DESKTOP_SCRIPT_SUFFIX#

const DESKTOP_SCRIPT_SUFFIX: ":desktop" = ":desktop"

The suffix marking a package.json script that belongs to the desktop variant.


TEMPLATE_ROOT_CANDIDATES#

const TEMPLATE_ROOT_CANDIDATES: readonly string[]

Where create-ignifx looks for its templates, in order.

Remarks#

Both entries are relative to the directory holding the running bin.js.

  • ../templates is the published layout: prepack copies templates/* into the package, so the tarball ships <package>/dist/bin.js beside <package>/templates/<name>.
  • ../../../templates is the development layout: packages/cli/dist/bin.js sits three directories below the repository root, where the real templates/ workspace members live. It is what makes node packages/cli/dist/bin.js my-game work from a checkout, without a pack.

USAGE#

const USAGE: "Usage: create-ignifx <target-dir> [--template <name>] [--overwrite] [--desktop]" = "Usage: create-ignifx <target-dir> [--template <name>] [--overwrite] [--desktop]"

The one-line usage string printed with every argument error.


VERSION#

const VERSION: "0.0.0" = "0.0.0"

The @ignifx/cli version this build was cut from. It is also the version a scaffolded project's @ignifx/* dependencies are pinned to, because the scope is released as one line (docs/architecture/00-overview.md §2).

Functions#

copyTemplate()#

copyTemplate(options): Promise<CopyTemplateResult>

Copies a template directory recursively into a target directory.

Parameters#

options#

CopyTemplateOptions

Source, destination, and the documented defaults in CopyTemplateOptions.

Returns#

Promise<CopyTemplateResult>

The relative paths of the files written, sorted.

Remarks#

The template tree is copied verbatim apart from four things: the rename map, the ignore list, package.json — whose workspace: dependency specifiers are rewritten to dependencyRange so that the generated project installs from the registry — and the desktop variant, which is skipped unless desktop is true. Symbolic links are followed and written as regular files, which is what a scaffolded project wants.

Throws#

A CliError with code IGX-1402 when templateDir is not an existing directory, or IGX-1401 when targetDir already holds entries and overwrite was not requested.

Example#

typescript
const result = await copyTemplate({  templateDir: "/path/to/templates/2d-topdown",  targetDir: "./my-game",});console.log(`${String(result.files.length)} files written`);

parseArgs()#

parseArgs(argv): CreateCommand

Parses a create-ignifx command line.

Parameters#

argv#

readonly string[]

Arguments after the executable and script name, as process.argv.slice(2) gives them.

Returns#

CreateCommand

The parsed command with defaults applied.

Remarks#

--desktop copies the template's Electron variant — its desktop/ directory, electron.vite.config.ts, electron-builder.yml, and the *:desktop scripts and dependencies that go with them. Without it the scaffold is browser-only, which is what keeps a browser game from downloading an Electron binary it never runs.

Throws#

A CliError with code IGX-1403 when the command line is unparseable, the target directory is missing, or extra positionals are given.

Example#

typescript
const command = parseArgs(["my-game", "--template", "3d-first-person", "--desktop"]);// { targetDir: "my-game", template: "3d-first-person", overwrite: false, desktop: true }

resolveTemplateDir()#

resolveTemplateDir(name, templatesRoot): Promise<string>

Maps a template name to its directory under templatesRoot.

Parameters#

name#

string

Template name, a single path segment such as "2d-topdown".

templatesRoot#

string

Directory that holds one subdirectory per template.

Returns#

Promise<string>

The absolute or relative path of the template directory, which is known to exist.

Throws#

A CliError with code IGX-1402 when name is not a single path segment or no such directory exists.


resolveTemplatesRoot()#

resolveTemplatesRoot(binDirectory): Promise<string>

Finds the directory that holds the templates.

Parameters#

binDirectory#

string

The directory of the running executable, normally import.meta.dirname.

Returns#

Promise<string>

The first of TEMPLATE_ROOT_CANDIDATES that exists as a directory; the first candidate when none does, so the failure names the published location rather than the checkout.

Example#

typescript
const templatesRoot = await resolveTemplatesRoot(import.meta.dirname);

runCreate()#

runCreate(argv, io): Promise<CopyTemplateResult>

Parses a command line, resolves the template, and copies it into the target directory.

Parameters#

argv#

readonly string[]

Arguments after the executable and script name.

io#

CreateIo

The injected output sinks and templates root.

Returns#

Promise<CopyTemplateResult>

The files written, relative to the target directory.

Throws#

A CliError for every expected failure; see parseArgs, resolveTemplateDir, and copyTemplate.

Example#

typescript
await runCreate(["my-game"], {  stdout: (line) => lines.push(line),  stderr: (line) => lines.push(line),  templatesRoot: "/path/to/templates",});