@flare-engine/create 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  //#region src/bin.ts
3
- require("./cli-JzwtwZhm.js").buildProgram().parseAsync(process.argv).catch((err) => {
3
+ require("./cli-uy56T6Kk.js").buildProgram().parseAsync(process.argv).catch((err) => {
4
4
  const msg = err instanceof Error ? err.message : String(err);
5
5
  process.stderr.write(`[flare-create] ${msg}\n`);
6
6
  process.exit(1);
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildProgram } from "./cli-BGXZ73ie.mjs";
2
+ import { t as buildProgram } from "./cli-BcE9XQJx.mjs";
3
3
  //#region src/bin.ts
4
4
  buildProgram().parseAsync(process.argv).catch((err) => {
5
5
  const msg = err instanceof Error ? err.message : String(err);
@@ -4,7 +4,7 @@ import { cp, mkdir, readFile, readdir, rename, stat, writeFile } from "node:fs/p
4
4
  import { dirname, isAbsolute, join, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  //#region package.json
7
- var version = "0.4.0";
7
+ var version = "0.4.1";
8
8
  //#endregion
9
9
  //#region src/manifest.ts
10
10
  /** Built-in templates shipped inside `@flare-engine/create`. */
@@ -186,4 +186,4 @@ function buildProgram() {
186
186
  //#endregion
187
187
  export { scaffoldApp as a, isValidAppName as i, detectPackageManager as n, TEMPLATES as o, installSpawnSpec as r, isTemplateId as s, buildProgram as t };
188
188
 
189
- //# sourceMappingURL=cli-BGXZ73ie.mjs.map
189
+ //# sourceMappingURL=cli-BcE9XQJx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-BGXZ73ie.mjs","names":[],"sources":["../package.json","../src/manifest.ts","../src/scaffold.ts","../src/cli.ts"],"sourcesContent":["","/** Declared metadata for a `@flare-engine/create` template. */\nexport interface TemplateManifest {\n /** Template directory name (`blank`, `topdown`, `shmup`, ...). */\n id: string;\n /** Human-readable name shown in logs. */\n displayName: string;\n /** One-line description of what the template produces. */\n description: string;\n /** Engines required — mirrors Node/npm engines field in scaffolded package.json. */\n engines: { node: string };\n /** Optional post-install instructions printed after scaffold completes. */\n postInstall?: string;\n}\n\n/** Built-in templates shipped inside `@flare-engine/create`. */\nexport const TEMPLATES = {\n blank: {\n id: 'blank',\n displayName: 'Blank',\n description: 'Empty scene with a single sprite — smallest starting point.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n topdown: {\n id: 'topdown',\n displayName: 'Top-down',\n description: 'Analog player movement with tilemap collision + camera follow.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n shmup: {\n id: 'shmup',\n displayName: 'Shoot-em-up',\n description:\n 'Auto-fire player, enemy spawner, HUD score — mirrors a minimum vertical-slice shmup.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n} satisfies Record<string, TemplateManifest>;\n\nexport type TemplateId = keyof typeof TEMPLATES;\n\nexport function isTemplateId(value: string): value is TemplateId {\n return Object.hasOwn(TEMPLATES, value);\n}\n","import { spawn } from 'node:child_process';\nimport { cp, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { version } from '../package.json';\nimport { isTemplateId, TEMPLATES, type TemplateId } from './manifest.js';\n\n/** Options for `scaffoldApp`. */\nexport interface ScaffoldOptions {\n /** App name — also used as the target directory and `name` in package.json. */\n name: string;\n /** Template id (default: `blank`). */\n template?: string;\n /** Destination directory (default: `<cwd>/<name>`). */\n targetDir?: string;\n /** Package manager used for `engines` + post-install command. */\n packageManager?: 'bun' | 'npm' | 'pnpm';\n /** Skip `install` after copying (default: false). */\n skipInstall?: boolean;\n /** Pinned Flare version for template {{FLARE_VERSION}} token (default: current package version prefixed with ^). */\n flareVersion?: string;\n /** Override templates root — used by tests. */\n templatesDir?: string;\n}\n\n/** Result returned by `scaffoldApp`. */\nexport interface ScaffoldResult {\n /** Absolute path of the scaffolded app. */\n targetDir: string;\n /** Resolved template id. */\n template: TemplateId;\n /** Package manager used. */\n packageManager: 'bun' | 'npm' | 'pnpm';\n /** Whether install was run. */\n installed: boolean;\n}\n\nconst FILENAME = fileURLToPath(import.meta.url);\nconst DEFAULT_TEMPLATES_DIR = resolve(dirname(FILENAME), '..', 'templates');\n\nconst VALID_APP_NAME = /^[a-z0-9@/_-]+$/i;\n\n/** Returns true when the name is safe to use as an app name and shell argument. */\nexport function isValidAppName(name: string): boolean {\n return VALID_APP_NAME.test(name);\n}\n\n/** Detect the package manager from `--package-manager` > `npm_config_user_agent` > `bun`. */\nexport function detectPackageManager(explicit: string | undefined): 'bun' | 'npm' | 'pnpm' {\n if (explicit === 'bun' || explicit === 'npm' || explicit === 'pnpm') return explicit;\n const agent = process.env.npm_config_user_agent ?? '';\n if (agent.startsWith('bun')) return 'bun';\n if (agent.startsWith('pnpm')) return 'pnpm';\n if (agent.startsWith('npm') || agent.startsWith('yarn')) return 'npm';\n return 'bun';\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nconst TOKEN_FILE_EXTS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.json',\n '.md',\n '.txt',\n '.html',\n '.css',\n]);\n\nfunction shouldApplyTokens(path: string): boolean {\n const dot = path.lastIndexOf('.');\n if (dot < 0) return false;\n return TOKEN_FILE_EXTS.has(path.slice(dot).toLowerCase());\n}\n\nfunction replaceTokens(content: string, tokens: Record<string, string>): string {\n let out = content;\n for (const [k, v] of Object.entries(tokens)) out = out.split(k).join(v);\n return out;\n}\n\nasync function walkFiles(dir: string, base: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const e of entries) {\n const full = join(dir, e.name);\n const rel = full.slice(base.length + 1);\n if (e.isDirectory()) {\n await walkFiles(full, base, out);\n } else if (e.isFile()) {\n out.push(rel);\n }\n }\n}\n\n/** Returns the spawn configuration for the given package manager on the given platform. */\nexport function installSpawnSpec(\n pm: 'bun' | 'npm' | 'pnpm',\n platform: NodeJS.Platform,\n): { cmd: string; shell: boolean } {\n return { cmd: pm, shell: platform === 'win32' };\n}\n\nfunction runInstall(pm: 'bun' | 'npm' | 'pnpm', cwd: string): Promise<number> {\n return new Promise((resolvePromise) => {\n const { cmd, shell } = installSpawnSpec(pm, process.platform);\n const child = spawn(cmd, ['install'], {\n cwd,\n stdio: 'inherit',\n shell,\n });\n child.on('exit', (code) => resolvePromise(code ?? 1));\n child.on('error', () => resolvePromise(1));\n });\n}\n\n/**\n * Copy the chosen template into `targetDir`, apply token replacements, and\n * optionally run the package manager's install. Fails if the target exists\n * and is non-empty.\n */\nexport async function scaffoldApp(opts: ScaffoldOptions): Promise<ScaffoldResult> {\n if (!isValidAppName(opts.name)) {\n throw new Error(`[flare-create] invalid app name: ${opts.name}`);\n }\n const templateId = opts.template ?? 'blank';\n if (!isTemplateId(templateId)) {\n throw new Error(`[flare-create] unknown template: ${templateId}`);\n }\n const manifest = TEMPLATES[templateId];\n if (!manifest) throw new Error(`[flare-create] missing manifest for ${templateId}`);\n\n const cwd = process.cwd();\n const target = opts.targetDir\n ? isAbsolute(opts.targetDir)\n ? opts.targetDir\n : resolve(cwd, opts.targetDir)\n : resolve(cwd, opts.name);\n\n if (await pathExists(target)) {\n const entries = await readdir(target);\n if (entries.length > 0) {\n throw new Error(`[flare-create] target ${target} already exists and is not empty`);\n }\n } else {\n await mkdir(target, { recursive: true });\n }\n\n const templatesRoot = opts.templatesDir ?? DEFAULT_TEMPLATES_DIR;\n const src = join(templatesRoot, templateId);\n if (!(await pathExists(src))) {\n throw new Error(`[flare-create] template directory not found: ${src}`);\n }\n\n await cp(src, target, { recursive: true });\n\n const gi = join(target, 'gitignore');\n if (await pathExists(gi)) {\n await rename(gi, join(target, '.gitignore'));\n }\n\n const tokens: Record<string, string> = {\n '{{APP_NAME}}': opts.name,\n '{{FLARE_VERSION}}': opts.flareVersion ?? `^${version}`,\n '{{TEMPLATE_ID}}': templateId,\n };\n\n const files: string[] = [];\n await walkFiles(target, target, files);\n for (const rel of files) {\n if (!shouldApplyTokens(rel)) continue;\n const p = join(target, rel);\n const content = await readFile(p, 'utf8');\n const next = replaceTokens(content, tokens);\n if (next !== content) await writeFile(p, next, 'utf8');\n }\n\n const pm = detectPackageManager(opts.packageManager);\n let installed = false;\n if (!opts.skipInstall) {\n const code = await runInstall(pm, target);\n if (code !== 0) {\n throw new Error(`[flare-create] ${pm} install exited with code ${code}`);\n }\n installed = true;\n }\n\n return {\n targetDir: target,\n template: templateId,\n packageManager: pm,\n installed,\n };\n}\n","import { Command } from 'commander';\nimport { version } from '../package.json';\nimport { TEMPLATES } from './manifest.js';\nimport { scaffoldApp } from './scaffold.js';\n\nfunction parseTemplateArg(value: string): 'blank' | 'topdown' | 'shmup' {\n if (value !== 'blank' && value !== 'topdown' && value !== 'shmup') {\n throw new Error(`invalid --template: ${value} (expected blank|topdown|shmup)`);\n }\n return value;\n}\n\nfunction parsePmArg(value: string): 'bun' | 'npm' | 'pnpm' {\n if (value !== 'bun' && value !== 'npm' && value !== 'pnpm') {\n throw new Error(`invalid --package-manager: ${value} (expected bun|npm|pnpm)`);\n }\n return value;\n}\n\n/** Build the `flare-create` CLI program. Exposed for tests. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('flare-create')\n .description('Scaffold a new Flare engine app')\n .version(version)\n .argument('<name>', 'directory name / package name for the new app')\n .option('-t, --template <name>', 'template (blank|topdown|shmup)', parseTemplateArg, 'blank')\n .option('-p, --package-manager <pm>', 'package manager (bun|npm|pnpm)', parsePmArg)\n .option('--skip-install', 'skip running install after scaffolding', false)\n .action(async (name: string, opts) => {\n const result = await scaffoldApp({\n name,\n template: opts.template,\n packageManager: opts.packageManager,\n skipInstall: opts.skipInstall === true,\n });\n const manifest = TEMPLATES[result.template];\n process.stdout.write(\n `[flare-create] scaffolded ${\n manifest?.displayName ?? result.template\n } at ${result.targetDir}\\n`,\n );\n if (!result.installed) {\n process.stdout.write(\n `[flare-create] skipped install — run \\`cd ${name} && ${result.packageManager} install\\` when ready\\n`,\n );\n } else if (manifest?.postInstall) {\n process.stdout.write(`[flare-create] next: cd ${name} && ${manifest.postInstall}\\n`);\n }\n });\n return program;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-BcE9XQJx.mjs","names":[],"sources":["../package.json","../src/manifest.ts","../src/scaffold.ts","../src/cli.ts"],"sourcesContent":["","/** Declared metadata for a `@flare-engine/create` template. */\nexport interface TemplateManifest {\n /** Template directory name (`blank`, `topdown`, `shmup`, ...). */\n id: string;\n /** Human-readable name shown in logs. */\n displayName: string;\n /** One-line description of what the template produces. */\n description: string;\n /** Engines required — mirrors Node/npm engines field in scaffolded package.json. */\n engines: { node: string };\n /** Optional post-install instructions printed after scaffold completes. */\n postInstall?: string;\n}\n\n/** Built-in templates shipped inside `@flare-engine/create`. */\nexport const TEMPLATES = {\n blank: {\n id: 'blank',\n displayName: 'Blank',\n description: 'Empty scene with a single sprite — smallest starting point.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n topdown: {\n id: 'topdown',\n displayName: 'Top-down',\n description: 'Analog player movement with tilemap collision + camera follow.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n shmup: {\n id: 'shmup',\n displayName: 'Shoot-em-up',\n description:\n 'Auto-fire player, enemy spawner, HUD score — mirrors a minimum vertical-slice shmup.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n} satisfies Record<string, TemplateManifest>;\n\nexport type TemplateId = keyof typeof TEMPLATES;\n\nexport function isTemplateId(value: string): value is TemplateId {\n return Object.hasOwn(TEMPLATES, value);\n}\n","import { spawn } from 'node:child_process';\nimport { cp, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { version } from '../package.json';\nimport { isTemplateId, TEMPLATES, type TemplateId } from './manifest.js';\n\n/** Options for `scaffoldApp`. */\nexport interface ScaffoldOptions {\n /** App name — also used as the target directory and `name` in package.json. */\n name: string;\n /** Template id (default: `blank`). */\n template?: string;\n /** Destination directory (default: `<cwd>/<name>`). */\n targetDir?: string;\n /** Package manager used for `engines` + post-install command. */\n packageManager?: 'bun' | 'npm' | 'pnpm';\n /** Skip `install` after copying (default: false). */\n skipInstall?: boolean;\n /** Pinned Flare version for template {{FLARE_VERSION}} token (default: current package version prefixed with ^). */\n flareVersion?: string;\n /** Override templates root — used by tests. */\n templatesDir?: string;\n}\n\n/** Result returned by `scaffoldApp`. */\nexport interface ScaffoldResult {\n /** Absolute path of the scaffolded app. */\n targetDir: string;\n /** Resolved template id. */\n template: TemplateId;\n /** Package manager used. */\n packageManager: 'bun' | 'npm' | 'pnpm';\n /** Whether install was run. */\n installed: boolean;\n}\n\nconst FILENAME = fileURLToPath(import.meta.url);\nconst DEFAULT_TEMPLATES_DIR = resolve(dirname(FILENAME), '..', 'templates');\n\nconst VALID_APP_NAME = /^[a-z0-9@/_-]+$/i;\n\n/** Returns true when the name is safe to use as an app name and shell argument. */\nexport function isValidAppName(name: string): boolean {\n return VALID_APP_NAME.test(name);\n}\n\n/** Detect the package manager from `--package-manager` > `npm_config_user_agent` > `bun`. */\nexport function detectPackageManager(explicit: string | undefined): 'bun' | 'npm' | 'pnpm' {\n if (explicit === 'bun' || explicit === 'npm' || explicit === 'pnpm') return explicit;\n const agent = process.env.npm_config_user_agent ?? '';\n if (agent.startsWith('bun')) return 'bun';\n if (agent.startsWith('pnpm')) return 'pnpm';\n if (agent.startsWith('npm') || agent.startsWith('yarn')) return 'npm';\n return 'bun';\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nconst TOKEN_FILE_EXTS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.json',\n '.md',\n '.txt',\n '.html',\n '.css',\n]);\n\nfunction shouldApplyTokens(path: string): boolean {\n const dot = path.lastIndexOf('.');\n if (dot < 0) return false;\n return TOKEN_FILE_EXTS.has(path.slice(dot).toLowerCase());\n}\n\nfunction replaceTokens(content: string, tokens: Record<string, string>): string {\n let out = content;\n for (const [k, v] of Object.entries(tokens)) out = out.split(k).join(v);\n return out;\n}\n\nasync function walkFiles(dir: string, base: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const e of entries) {\n const full = join(dir, e.name);\n const rel = full.slice(base.length + 1);\n if (e.isDirectory()) {\n await walkFiles(full, base, out);\n } else if (e.isFile()) {\n out.push(rel);\n }\n }\n}\n\n/** Returns the spawn configuration for the given package manager on the given platform. */\nexport function installSpawnSpec(\n pm: 'bun' | 'npm' | 'pnpm',\n platform: NodeJS.Platform,\n): { cmd: string; shell: boolean } {\n return { cmd: pm, shell: platform === 'win32' };\n}\n\nfunction runInstall(pm: 'bun' | 'npm' | 'pnpm', cwd: string): Promise<number> {\n return new Promise((resolvePromise) => {\n const { cmd, shell } = installSpawnSpec(pm, process.platform);\n const child = spawn(cmd, ['install'], {\n cwd,\n stdio: 'inherit',\n shell,\n });\n child.on('exit', (code) => resolvePromise(code ?? 1));\n child.on('error', () => resolvePromise(1));\n });\n}\n\n/**\n * Copy the chosen template into `targetDir`, apply token replacements, and\n * optionally run the package manager's install. Fails if the target exists\n * and is non-empty.\n */\nexport async function scaffoldApp(opts: ScaffoldOptions): Promise<ScaffoldResult> {\n if (!isValidAppName(opts.name)) {\n throw new Error(`[flare-create] invalid app name: ${opts.name}`);\n }\n const templateId = opts.template ?? 'blank';\n if (!isTemplateId(templateId)) {\n throw new Error(`[flare-create] unknown template: ${templateId}`);\n }\n const manifest = TEMPLATES[templateId];\n if (!manifest) throw new Error(`[flare-create] missing manifest for ${templateId}`);\n\n const cwd = process.cwd();\n const target = opts.targetDir\n ? isAbsolute(opts.targetDir)\n ? opts.targetDir\n : resolve(cwd, opts.targetDir)\n : resolve(cwd, opts.name);\n\n if (await pathExists(target)) {\n const entries = await readdir(target);\n if (entries.length > 0) {\n throw new Error(`[flare-create] target ${target} already exists and is not empty`);\n }\n } else {\n await mkdir(target, { recursive: true });\n }\n\n const templatesRoot = opts.templatesDir ?? DEFAULT_TEMPLATES_DIR;\n const src = join(templatesRoot, templateId);\n if (!(await pathExists(src))) {\n throw new Error(`[flare-create] template directory not found: ${src}`);\n }\n\n await cp(src, target, { recursive: true });\n\n const gi = join(target, 'gitignore');\n if (await pathExists(gi)) {\n await rename(gi, join(target, '.gitignore'));\n }\n\n const tokens: Record<string, string> = {\n '{{APP_NAME}}': opts.name,\n '{{FLARE_VERSION}}': opts.flareVersion ?? `^${version}`,\n '{{TEMPLATE_ID}}': templateId,\n };\n\n const files: string[] = [];\n await walkFiles(target, target, files);\n for (const rel of files) {\n if (!shouldApplyTokens(rel)) continue;\n const p = join(target, rel);\n const content = await readFile(p, 'utf8');\n const next = replaceTokens(content, tokens);\n if (next !== content) await writeFile(p, next, 'utf8');\n }\n\n const pm = detectPackageManager(opts.packageManager);\n let installed = false;\n if (!opts.skipInstall) {\n const code = await runInstall(pm, target);\n if (code !== 0) {\n throw new Error(`[flare-create] ${pm} install exited with code ${code}`);\n }\n installed = true;\n }\n\n return {\n targetDir: target,\n template: templateId,\n packageManager: pm,\n installed,\n };\n}\n","import { Command } from 'commander';\nimport { version } from '../package.json';\nimport { TEMPLATES } from './manifest.js';\nimport { scaffoldApp } from './scaffold.js';\n\nfunction parseTemplateArg(value: string): 'blank' | 'topdown' | 'shmup' {\n if (value !== 'blank' && value !== 'topdown' && value !== 'shmup') {\n throw new Error(`invalid --template: ${value} (expected blank|topdown|shmup)`);\n }\n return value;\n}\n\nfunction parsePmArg(value: string): 'bun' | 'npm' | 'pnpm' {\n if (value !== 'bun' && value !== 'npm' && value !== 'pnpm') {\n throw new Error(`invalid --package-manager: ${value} (expected bun|npm|pnpm)`);\n }\n return value;\n}\n\n/** Build the `flare-create` CLI program. Exposed for tests. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('flare-create')\n .description('Scaffold a new Flare engine app')\n .version(version)\n .argument('<name>', 'directory name / package name for the new app')\n .option('-t, --template <name>', 'template (blank|topdown|shmup)', parseTemplateArg, 'blank')\n .option('-p, --package-manager <pm>', 'package manager (bun|npm|pnpm)', parsePmArg)\n .option('--skip-install', 'skip running install after scaffolding', false)\n .action(async (name: string, opts) => {\n const result = await scaffoldApp({\n name,\n template: opts.template,\n packageManager: opts.packageManager,\n skipInstall: opts.skipInstall === true,\n });\n const manifest = TEMPLATES[result.template];\n process.stdout.write(\n `[flare-create] scaffolded ${\n manifest?.displayName ?? result.template\n } at ${result.targetDir}\\n`,\n );\n if (!result.installed) {\n process.stdout.write(\n `[flare-create] skipped install — run \\`cd ${name} && ${result.packageManager} install\\` when ready\\n`,\n );\n } else if (manifest?.postInstall) {\n process.stdout.write(`[flare-create] next: cd ${name} && ${manifest.postInstall}\\n`);\n }\n });\n return program;\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ let node_fs_promises = require("node:fs/promises");
4
4
  let node_path = require("node:path");
5
5
  let node_url = require("node:url");
6
6
  //#region package.json
7
- var version = "0.4.0";
7
+ var version = "0.4.1";
8
8
  //#endregion
9
9
  //#region src/manifest.ts
10
10
  /** Built-in templates shipped inside `@flare-engine/create`. */
@@ -227,4 +227,4 @@ Object.defineProperty(exports, "scaffoldApp", {
227
227
  }
228
228
  });
229
229
 
230
- //# sourceMappingURL=cli-JzwtwZhm.js.map
230
+ //# sourceMappingURL=cli-uy56T6Kk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-JzwtwZhm.js","names":["Command"],"sources":["../package.json","../src/manifest.ts","../src/scaffold.ts","../src/cli.ts"],"sourcesContent":["","/** Declared metadata for a `@flare-engine/create` template. */\nexport interface TemplateManifest {\n /** Template directory name (`blank`, `topdown`, `shmup`, ...). */\n id: string;\n /** Human-readable name shown in logs. */\n displayName: string;\n /** One-line description of what the template produces. */\n description: string;\n /** Engines required — mirrors Node/npm engines field in scaffolded package.json. */\n engines: { node: string };\n /** Optional post-install instructions printed after scaffold completes. */\n postInstall?: string;\n}\n\n/** Built-in templates shipped inside `@flare-engine/create`. */\nexport const TEMPLATES = {\n blank: {\n id: 'blank',\n displayName: 'Blank',\n description: 'Empty scene with a single sprite — smallest starting point.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n topdown: {\n id: 'topdown',\n displayName: 'Top-down',\n description: 'Analog player movement with tilemap collision + camera follow.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n shmup: {\n id: 'shmup',\n displayName: 'Shoot-em-up',\n description:\n 'Auto-fire player, enemy spawner, HUD score — mirrors a minimum vertical-slice shmup.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n} satisfies Record<string, TemplateManifest>;\n\nexport type TemplateId = keyof typeof TEMPLATES;\n\nexport function isTemplateId(value: string): value is TemplateId {\n return Object.hasOwn(TEMPLATES, value);\n}\n","import { spawn } from 'node:child_process';\nimport { cp, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { version } from '../package.json';\nimport { isTemplateId, TEMPLATES, type TemplateId } from './manifest.js';\n\n/** Options for `scaffoldApp`. */\nexport interface ScaffoldOptions {\n /** App name — also used as the target directory and `name` in package.json. */\n name: string;\n /** Template id (default: `blank`). */\n template?: string;\n /** Destination directory (default: `<cwd>/<name>`). */\n targetDir?: string;\n /** Package manager used for `engines` + post-install command. */\n packageManager?: 'bun' | 'npm' | 'pnpm';\n /** Skip `install` after copying (default: false). */\n skipInstall?: boolean;\n /** Pinned Flare version for template {{FLARE_VERSION}} token (default: current package version prefixed with ^). */\n flareVersion?: string;\n /** Override templates root — used by tests. */\n templatesDir?: string;\n}\n\n/** Result returned by `scaffoldApp`. */\nexport interface ScaffoldResult {\n /** Absolute path of the scaffolded app. */\n targetDir: string;\n /** Resolved template id. */\n template: TemplateId;\n /** Package manager used. */\n packageManager: 'bun' | 'npm' | 'pnpm';\n /** Whether install was run. */\n installed: boolean;\n}\n\nconst FILENAME = fileURLToPath(import.meta.url);\nconst DEFAULT_TEMPLATES_DIR = resolve(dirname(FILENAME), '..', 'templates');\n\nconst VALID_APP_NAME = /^[a-z0-9@/_-]+$/i;\n\n/** Returns true when the name is safe to use as an app name and shell argument. */\nexport function isValidAppName(name: string): boolean {\n return VALID_APP_NAME.test(name);\n}\n\n/** Detect the package manager from `--package-manager` > `npm_config_user_agent` > `bun`. */\nexport function detectPackageManager(explicit: string | undefined): 'bun' | 'npm' | 'pnpm' {\n if (explicit === 'bun' || explicit === 'npm' || explicit === 'pnpm') return explicit;\n const agent = process.env.npm_config_user_agent ?? '';\n if (agent.startsWith('bun')) return 'bun';\n if (agent.startsWith('pnpm')) return 'pnpm';\n if (agent.startsWith('npm') || agent.startsWith('yarn')) return 'npm';\n return 'bun';\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nconst TOKEN_FILE_EXTS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.json',\n '.md',\n '.txt',\n '.html',\n '.css',\n]);\n\nfunction shouldApplyTokens(path: string): boolean {\n const dot = path.lastIndexOf('.');\n if (dot < 0) return false;\n return TOKEN_FILE_EXTS.has(path.slice(dot).toLowerCase());\n}\n\nfunction replaceTokens(content: string, tokens: Record<string, string>): string {\n let out = content;\n for (const [k, v] of Object.entries(tokens)) out = out.split(k).join(v);\n return out;\n}\n\nasync function walkFiles(dir: string, base: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const e of entries) {\n const full = join(dir, e.name);\n const rel = full.slice(base.length + 1);\n if (e.isDirectory()) {\n await walkFiles(full, base, out);\n } else if (e.isFile()) {\n out.push(rel);\n }\n }\n}\n\n/** Returns the spawn configuration for the given package manager on the given platform. */\nexport function installSpawnSpec(\n pm: 'bun' | 'npm' | 'pnpm',\n platform: NodeJS.Platform,\n): { cmd: string; shell: boolean } {\n return { cmd: pm, shell: platform === 'win32' };\n}\n\nfunction runInstall(pm: 'bun' | 'npm' | 'pnpm', cwd: string): Promise<number> {\n return new Promise((resolvePromise) => {\n const { cmd, shell } = installSpawnSpec(pm, process.platform);\n const child = spawn(cmd, ['install'], {\n cwd,\n stdio: 'inherit',\n shell,\n });\n child.on('exit', (code) => resolvePromise(code ?? 1));\n child.on('error', () => resolvePromise(1));\n });\n}\n\n/**\n * Copy the chosen template into `targetDir`, apply token replacements, and\n * optionally run the package manager's install. Fails if the target exists\n * and is non-empty.\n */\nexport async function scaffoldApp(opts: ScaffoldOptions): Promise<ScaffoldResult> {\n if (!isValidAppName(opts.name)) {\n throw new Error(`[flare-create] invalid app name: ${opts.name}`);\n }\n const templateId = opts.template ?? 'blank';\n if (!isTemplateId(templateId)) {\n throw new Error(`[flare-create] unknown template: ${templateId}`);\n }\n const manifest = TEMPLATES[templateId];\n if (!manifest) throw new Error(`[flare-create] missing manifest for ${templateId}`);\n\n const cwd = process.cwd();\n const target = opts.targetDir\n ? isAbsolute(opts.targetDir)\n ? opts.targetDir\n : resolve(cwd, opts.targetDir)\n : resolve(cwd, opts.name);\n\n if (await pathExists(target)) {\n const entries = await readdir(target);\n if (entries.length > 0) {\n throw new Error(`[flare-create] target ${target} already exists and is not empty`);\n }\n } else {\n await mkdir(target, { recursive: true });\n }\n\n const templatesRoot = opts.templatesDir ?? DEFAULT_TEMPLATES_DIR;\n const src = join(templatesRoot, templateId);\n if (!(await pathExists(src))) {\n throw new Error(`[flare-create] template directory not found: ${src}`);\n }\n\n await cp(src, target, { recursive: true });\n\n const gi = join(target, 'gitignore');\n if (await pathExists(gi)) {\n await rename(gi, join(target, '.gitignore'));\n }\n\n const tokens: Record<string, string> = {\n '{{APP_NAME}}': opts.name,\n '{{FLARE_VERSION}}': opts.flareVersion ?? `^${version}`,\n '{{TEMPLATE_ID}}': templateId,\n };\n\n const files: string[] = [];\n await walkFiles(target, target, files);\n for (const rel of files) {\n if (!shouldApplyTokens(rel)) continue;\n const p = join(target, rel);\n const content = await readFile(p, 'utf8');\n const next = replaceTokens(content, tokens);\n if (next !== content) await writeFile(p, next, 'utf8');\n }\n\n const pm = detectPackageManager(opts.packageManager);\n let installed = false;\n if (!opts.skipInstall) {\n const code = await runInstall(pm, target);\n if (code !== 0) {\n throw new Error(`[flare-create] ${pm} install exited with code ${code}`);\n }\n installed = true;\n }\n\n return {\n targetDir: target,\n template: templateId,\n packageManager: pm,\n installed,\n };\n}\n","import { Command } from 'commander';\nimport { version } from '../package.json';\nimport { TEMPLATES } from './manifest.js';\nimport { scaffoldApp } from './scaffold.js';\n\nfunction parseTemplateArg(value: string): 'blank' | 'topdown' | 'shmup' {\n if (value !== 'blank' && value !== 'topdown' && value !== 'shmup') {\n throw new Error(`invalid --template: ${value} (expected blank|topdown|shmup)`);\n }\n return value;\n}\n\nfunction parsePmArg(value: string): 'bun' | 'npm' | 'pnpm' {\n if (value !== 'bun' && value !== 'npm' && value !== 'pnpm') {\n throw new Error(`invalid --package-manager: ${value} (expected bun|npm|pnpm)`);\n }\n return value;\n}\n\n/** Build the `flare-create` CLI program. Exposed for tests. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('flare-create')\n .description('Scaffold a new Flare engine app')\n .version(version)\n .argument('<name>', 'directory name / package name for the new app')\n .option('-t, --template <name>', 'template (blank|topdown|shmup)', parseTemplateArg, 'blank')\n .option('-p, --package-manager <pm>', 'package manager (bun|npm|pnpm)', parsePmArg)\n .option('--skip-install', 'skip running install after scaffolding', false)\n .action(async (name: string, opts) => {\n const result = await scaffoldApp({\n name,\n template: opts.template,\n packageManager: opts.packageManager,\n skipInstall: opts.skipInstall === true,\n });\n const manifest = TEMPLATES[result.template];\n process.stdout.write(\n `[flare-create] scaffolded ${\n manifest?.displayName ?? result.template\n } at ${result.targetDir}\\n`,\n );\n if (!result.installed) {\n process.stdout.write(\n `[flare-create] skipped install — run \\`cd ${name} && ${result.packageManager} install\\` when ready\\n`,\n );\n } else if (manifest?.postInstall) {\n process.stdout.write(`[flare-create] next: cd ${name} && ${manifest.postInstall}\\n`);\n }\n });\n return program;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-uy56T6Kk.js","names":["Command"],"sources":["../package.json","../src/manifest.ts","../src/scaffold.ts","../src/cli.ts"],"sourcesContent":["","/** Declared metadata for a `@flare-engine/create` template. */\nexport interface TemplateManifest {\n /** Template directory name (`blank`, `topdown`, `shmup`, ...). */\n id: string;\n /** Human-readable name shown in logs. */\n displayName: string;\n /** One-line description of what the template produces. */\n description: string;\n /** Engines required — mirrors Node/npm engines field in scaffolded package.json. */\n engines: { node: string };\n /** Optional post-install instructions printed after scaffold completes. */\n postInstall?: string;\n}\n\n/** Built-in templates shipped inside `@flare-engine/create`. */\nexport const TEMPLATES = {\n blank: {\n id: 'blank',\n displayName: 'Blank',\n description: 'Empty scene with a single sprite — smallest starting point.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n topdown: {\n id: 'topdown',\n displayName: 'Top-down',\n description: 'Analog player movement with tilemap collision + camera follow.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n shmup: {\n id: 'shmup',\n displayName: 'Shoot-em-up',\n description:\n 'Auto-fire player, enemy spawner, HUD score — mirrors a minimum vertical-slice shmup.',\n engines: { node: '>=20' },\n postInstall: 'bun run start',\n },\n} satisfies Record<string, TemplateManifest>;\n\nexport type TemplateId = keyof typeof TEMPLATES;\n\nexport function isTemplateId(value: string): value is TemplateId {\n return Object.hasOwn(TEMPLATES, value);\n}\n","import { spawn } from 'node:child_process';\nimport { cp, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { version } from '../package.json';\nimport { isTemplateId, TEMPLATES, type TemplateId } from './manifest.js';\n\n/** Options for `scaffoldApp`. */\nexport interface ScaffoldOptions {\n /** App name — also used as the target directory and `name` in package.json. */\n name: string;\n /** Template id (default: `blank`). */\n template?: string;\n /** Destination directory (default: `<cwd>/<name>`). */\n targetDir?: string;\n /** Package manager used for `engines` + post-install command. */\n packageManager?: 'bun' | 'npm' | 'pnpm';\n /** Skip `install` after copying (default: false). */\n skipInstall?: boolean;\n /** Pinned Flare version for template {{FLARE_VERSION}} token (default: current package version prefixed with ^). */\n flareVersion?: string;\n /** Override templates root — used by tests. */\n templatesDir?: string;\n}\n\n/** Result returned by `scaffoldApp`. */\nexport interface ScaffoldResult {\n /** Absolute path of the scaffolded app. */\n targetDir: string;\n /** Resolved template id. */\n template: TemplateId;\n /** Package manager used. */\n packageManager: 'bun' | 'npm' | 'pnpm';\n /** Whether install was run. */\n installed: boolean;\n}\n\nconst FILENAME = fileURLToPath(import.meta.url);\nconst DEFAULT_TEMPLATES_DIR = resolve(dirname(FILENAME), '..', 'templates');\n\nconst VALID_APP_NAME = /^[a-z0-9@/_-]+$/i;\n\n/** Returns true when the name is safe to use as an app name and shell argument. */\nexport function isValidAppName(name: string): boolean {\n return VALID_APP_NAME.test(name);\n}\n\n/** Detect the package manager from `--package-manager` > `npm_config_user_agent` > `bun`. */\nexport function detectPackageManager(explicit: string | undefined): 'bun' | 'npm' | 'pnpm' {\n if (explicit === 'bun' || explicit === 'npm' || explicit === 'pnpm') return explicit;\n const agent = process.env.npm_config_user_agent ?? '';\n if (agent.startsWith('bun')) return 'bun';\n if (agent.startsWith('pnpm')) return 'pnpm';\n if (agent.startsWith('npm') || agent.startsWith('yarn')) return 'npm';\n return 'bun';\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nconst TOKEN_FILE_EXTS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.json',\n '.md',\n '.txt',\n '.html',\n '.css',\n]);\n\nfunction shouldApplyTokens(path: string): boolean {\n const dot = path.lastIndexOf('.');\n if (dot < 0) return false;\n return TOKEN_FILE_EXTS.has(path.slice(dot).toLowerCase());\n}\n\nfunction replaceTokens(content: string, tokens: Record<string, string>): string {\n let out = content;\n for (const [k, v] of Object.entries(tokens)) out = out.split(k).join(v);\n return out;\n}\n\nasync function walkFiles(dir: string, base: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const e of entries) {\n const full = join(dir, e.name);\n const rel = full.slice(base.length + 1);\n if (e.isDirectory()) {\n await walkFiles(full, base, out);\n } else if (e.isFile()) {\n out.push(rel);\n }\n }\n}\n\n/** Returns the spawn configuration for the given package manager on the given platform. */\nexport function installSpawnSpec(\n pm: 'bun' | 'npm' | 'pnpm',\n platform: NodeJS.Platform,\n): { cmd: string; shell: boolean } {\n return { cmd: pm, shell: platform === 'win32' };\n}\n\nfunction runInstall(pm: 'bun' | 'npm' | 'pnpm', cwd: string): Promise<number> {\n return new Promise((resolvePromise) => {\n const { cmd, shell } = installSpawnSpec(pm, process.platform);\n const child = spawn(cmd, ['install'], {\n cwd,\n stdio: 'inherit',\n shell,\n });\n child.on('exit', (code) => resolvePromise(code ?? 1));\n child.on('error', () => resolvePromise(1));\n });\n}\n\n/**\n * Copy the chosen template into `targetDir`, apply token replacements, and\n * optionally run the package manager's install. Fails if the target exists\n * and is non-empty.\n */\nexport async function scaffoldApp(opts: ScaffoldOptions): Promise<ScaffoldResult> {\n if (!isValidAppName(opts.name)) {\n throw new Error(`[flare-create] invalid app name: ${opts.name}`);\n }\n const templateId = opts.template ?? 'blank';\n if (!isTemplateId(templateId)) {\n throw new Error(`[flare-create] unknown template: ${templateId}`);\n }\n const manifest = TEMPLATES[templateId];\n if (!manifest) throw new Error(`[flare-create] missing manifest for ${templateId}`);\n\n const cwd = process.cwd();\n const target = opts.targetDir\n ? isAbsolute(opts.targetDir)\n ? opts.targetDir\n : resolve(cwd, opts.targetDir)\n : resolve(cwd, opts.name);\n\n if (await pathExists(target)) {\n const entries = await readdir(target);\n if (entries.length > 0) {\n throw new Error(`[flare-create] target ${target} already exists and is not empty`);\n }\n } else {\n await mkdir(target, { recursive: true });\n }\n\n const templatesRoot = opts.templatesDir ?? DEFAULT_TEMPLATES_DIR;\n const src = join(templatesRoot, templateId);\n if (!(await pathExists(src))) {\n throw new Error(`[flare-create] template directory not found: ${src}`);\n }\n\n await cp(src, target, { recursive: true });\n\n const gi = join(target, 'gitignore');\n if (await pathExists(gi)) {\n await rename(gi, join(target, '.gitignore'));\n }\n\n const tokens: Record<string, string> = {\n '{{APP_NAME}}': opts.name,\n '{{FLARE_VERSION}}': opts.flareVersion ?? `^${version}`,\n '{{TEMPLATE_ID}}': templateId,\n };\n\n const files: string[] = [];\n await walkFiles(target, target, files);\n for (const rel of files) {\n if (!shouldApplyTokens(rel)) continue;\n const p = join(target, rel);\n const content = await readFile(p, 'utf8');\n const next = replaceTokens(content, tokens);\n if (next !== content) await writeFile(p, next, 'utf8');\n }\n\n const pm = detectPackageManager(opts.packageManager);\n let installed = false;\n if (!opts.skipInstall) {\n const code = await runInstall(pm, target);\n if (code !== 0) {\n throw new Error(`[flare-create] ${pm} install exited with code ${code}`);\n }\n installed = true;\n }\n\n return {\n targetDir: target,\n template: templateId,\n packageManager: pm,\n installed,\n };\n}\n","import { Command } from 'commander';\nimport { version } from '../package.json';\nimport { TEMPLATES } from './manifest.js';\nimport { scaffoldApp } from './scaffold.js';\n\nfunction parseTemplateArg(value: string): 'blank' | 'topdown' | 'shmup' {\n if (value !== 'blank' && value !== 'topdown' && value !== 'shmup') {\n throw new Error(`invalid --template: ${value} (expected blank|topdown|shmup)`);\n }\n return value;\n}\n\nfunction parsePmArg(value: string): 'bun' | 'npm' | 'pnpm' {\n if (value !== 'bun' && value !== 'npm' && value !== 'pnpm') {\n throw new Error(`invalid --package-manager: ${value} (expected bun|npm|pnpm)`);\n }\n return value;\n}\n\n/** Build the `flare-create` CLI program. Exposed for tests. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('flare-create')\n .description('Scaffold a new Flare engine app')\n .version(version)\n .argument('<name>', 'directory name / package name for the new app')\n .option('-t, --template <name>', 'template (blank|topdown|shmup)', parseTemplateArg, 'blank')\n .option('-p, --package-manager <pm>', 'package manager (bun|npm|pnpm)', parsePmArg)\n .option('--skip-install', 'skip running install after scaffolding', false)\n .action(async (name: string, opts) => {\n const result = await scaffoldApp({\n name,\n template: opts.template,\n packageManager: opts.packageManager,\n skipInstall: opts.skipInstall === true,\n });\n const manifest = TEMPLATES[result.template];\n process.stdout.write(\n `[flare-create] scaffolded ${\n manifest?.displayName ?? result.template\n } at ${result.targetDir}\\n`,\n );\n if (!result.installed) {\n process.stdout.write(\n `[flare-create] skipped install — run \\`cd ${name} && ${result.packageManager} install\\` when ready\\n`,\n );\n } else if (manifest?.postInstall) {\n process.stdout.write(`[flare-create] next: cd ${name} && ${manifest.postInstall}\\n`);\n }\n });\n return program;\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_cli = require("./cli-JzwtwZhm.js");
2
+ const require_cli = require("./cli-uy56T6Kk.js");
3
3
  exports.TEMPLATES = require_cli.TEMPLATES;
4
4
  exports.buildProgram = require_cli.buildProgram;
5
5
  exports.detectPackageManager = require_cli.detectPackageManager;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as scaffoldApp, i as isValidAppName, n as detectPackageManager, o as TEMPLATES, r as installSpawnSpec, s as isTemplateId, t as buildProgram } from "./cli-BGXZ73ie.mjs";
1
+ import { a as scaffoldApp, i as isValidAppName, n as detectPackageManager, o as TEMPLATES, r as installSpawnSpec, s as isTemplateId, t as buildProgram } from "./cli-BcE9XQJx.mjs";
2
2
  export { TEMPLATES, buildProgram, detectPackageManager, installSpawnSpec, isTemplateId, isValidAppName, scaffoldApp };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flare-engine/create",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "One-shot scaffolder for Flare engine apps — `npm create @flare-engine`",
5
5
  "keywords": ["flare-engine", "scaffold", "create-app", "expo", "react-native"],
6
6
  "license": "Apache-2.0",
@@ -43,12 +43,12 @@
43
43
  "commander": "^13.1.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@flare-engine/ecs": "^0.4.0",
47
- "@flare-engine/engine": "^0.4.0",
48
- "@flare-engine/physics": "^0.4.0",
49
- "@flare-engine/react": "^0.4.0",
50
- "@flare-engine/render": "^0.4.0",
51
- "@flare-engine/tilemap": "^0.4.0"
46
+ "@flare-engine/ecs": "^0.4.1",
47
+ "@flare-engine/engine": "^0.4.1",
48
+ "@flare-engine/physics": "^0.4.1",
49
+ "@flare-engine/react": "^0.4.1",
50
+ "@flare-engine/render": "^0.4.1",
51
+ "@flare-engine/tilemap": "^0.4.1"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=20"