@cellajs/create-cella 0.3.2 → 0.3.4

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/README.md CHANGED
@@ -23,13 +23,11 @@ pnpm create @cellajs/cella [directory] [options]
23
23
 
24
24
  ## What It Does
25
25
 
26
- 1. Downloads the latest Cella template as a GitHub tarball and extracts it in-process (no `git` binary, no shell)
27
- 2. Cleans template files (removes cella-specific docs, configs) and interpolates the project config
28
- 3. Initializes a git repository with an initial commit via isomorphic-git
29
- 4. Creates an optional working branch
30
- 5. Adds Cella as the upstream remote for future syncs
31
-
32
- Dependency installation and migration generation are **not** run automatically — after scaffolding, run `pnpm install`, then `pnpm generate` (the CLI prints these next steps on success).
26
+ 1. Creates a new project from a Cella template snapshot
27
+ 2. Lets you choose optional modules, ports, and the seed admin email
28
+ 3. Cleans the template and generates project-specific config
29
+ 4. Initializes a fresh git repository with an initial commit
30
+ 5. Adds the Cella upstream remote for future syncs
33
31
 
34
32
  ## Development
35
33
 
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/create-cella-cli.ts
4
4
  import { existsSync as existsSync2 } from "fs";
5
+ import { rm as rm3 } from "fs/promises";
5
6
  import { basename as basename2, resolve as resolve3 } from "path";
6
7
  import { input, select } from "@inquirer/prompts";
7
8
  import pc6 from "picocolors";
@@ -12,7 +13,7 @@ import pc from "picocolors";
12
13
  // package.json
13
14
  var package_default = {
14
15
  name: "@cellajs/create-cella",
15
- version: "0.3.2",
16
+ version: "0.3.4",
16
17
  private: false,
17
18
  license: "MIT",
18
19
  description: "Cella is a TypeScript template to create web apps with sync and offline capabilities.",
@@ -111,7 +112,9 @@ var package_default = {
111
112
  var NAME = "create cella";
112
113
  var DIVIDER = "\u2500".repeat(60);
113
114
  var TEMPLATE_URL = "github:cellajs/cella";
115
+ var TEMPLATE_REPOSITORY = "cellajs/cella";
114
116
  var CELLA_REMOTE_URL = "git@github.com:cellajs/cella.git";
117
+ var CELLA_REMOTE_NAME = "cella-upstream";
115
118
  var DESCRIPTION = package_default.description;
116
119
  var VERSION = package_default.version;
117
120
  var WEBSITE = package_default.homepage;
@@ -122,13 +125,14 @@ function getHeaderLine() {
122
125
  const padding = Math.max(1, 60 - leftText.length - WEBSITE.length);
123
126
  return `${left}${" ".repeat(padding)}${right}`;
124
127
  }
125
- var TO_REMOVE = ["./cli/create", "./info/QUICKSTART.md"];
128
+ var TO_REMOVE = ["./infra/Pulumi.production.yaml"];
126
129
  var TO_CLEAN = ["./backend/drizzle"];
127
130
  var TO_COPY = {
128
131
  "./frontend/.env.example": "./frontend/.env",
129
- "./info/QUICKSTART.md": "README.md"
132
+ "./cella/QUICKSTART.md": "README.md"
130
133
  };
131
134
  var PLACEHOLDER_CONFIG = "shared/config/config.template.ts";
135
+ var INITIAL_VERSION = "0.0.0";
132
136
  async function generateEnvFromExample(examplePath, replacements) {
133
137
  const { readFile: readFile4 } = await import("fs/promises");
134
138
  let content;
@@ -218,8 +222,8 @@ ${nameEntry}${body}
218
222
 
219
223
  // src/create.ts
220
224
  import { existsSync } from "fs";
221
- import { cp, mkdir, rm as rm2 } from "fs/promises";
222
- import { dirname as dirname2, isAbsolute, join as join4, relative as relative2, resolve as resolve2 } from "path";
225
+ import { cp, mkdir as mkdir2, rm as rm2 } from "fs/promises";
226
+ import { dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve as resolve2 } from "path";
223
227
  import { checkbox } from "@inquirer/prompts";
224
228
  import pc5 from "picocolors";
225
229
 
@@ -253,7 +257,7 @@ async function gitRemoteRemove(repoPath, remoteName) {
253
257
  async function addRemote({
254
258
  targetFolder,
255
259
  remoteUrl = CELLA_REMOTE_URL,
256
- remoteName = "upstream",
260
+ remoteName = CELLA_REMOTE_NAME,
257
261
  silent = false
258
262
  }) {
259
263
  try {
@@ -394,6 +398,7 @@ async function cleanTemplate({
394
398
  await copyFile(srcAbsolutePath, destAbsolutePath);
395
399
  }
396
400
  await applyPlaceholderConfig(targetFolder, projectName, displayName);
401
+ await resetReleaseState(targetFolder, projectName);
397
402
  const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);
398
403
  const backendEnv = await generateEnvFromExample(
399
404
  path.resolve(targetFolder, "backend/.env.example"),
@@ -472,6 +477,51 @@ ${warningMark} Fork config template "${src}" not found > Skip`);
472
477
  }
473
478
  }
474
479
  }
480
+ async function resetReleaseState(targetFolder, projectName) {
481
+ const pkgPath = path.resolve(targetFolder, "package.json");
482
+ try {
483
+ let pkg = await fs2.readFile(pkgPath, "utf8");
484
+ pkg = pkg.replace(/^(\s*"name":\s*)"[^"]*"/m, `$1"${projectName}"`);
485
+ pkg = pkg.replace(/^(\s*"version":\s*)"[^"]*"/m, `$1"${INITIAL_VERSION}"`);
486
+ for (const field of ["license", "repository", "description", "author", "homepage"]) {
487
+ pkg = pkg.replace(new RegExp(`^(\\s*"${field}":\\s*)"[^"]*"`, "m"), '$1""');
488
+ }
489
+ pkg = pkg.replace(/^(\s*"keywords":\s*)\[[^\]]*\]/m, "$1[]");
490
+ await fs2.writeFile(pkgPath, pkg, "utf8");
491
+ } catch (err) {
492
+ if (err.code === "ENOENT") {
493
+ console.info(`
494
+ ${warningMark} "package.json" not found > Skip version reset`);
495
+ } else {
496
+ throw err;
497
+ }
498
+ }
499
+ const configPath = path.resolve(targetFolder, ".github/release-please-config.json");
500
+ try {
501
+ const config = JSON.parse(await fs2.readFile(configPath, "utf8"));
502
+ config.packages ??= {};
503
+ config.packages["."] ??= {};
504
+ config.packages["."]["changelog-path"] = "CHANGELOG.md";
505
+ await fs2.writeFile(configPath, `${JSON.stringify(config, null, 2)}
506
+ `, "utf8");
507
+ } catch (err) {
508
+ if (err.code === "ENOENT") {
509
+ console.info(`
510
+ ${warningMark} "release-please-config.json" not found > Skip changelog path reset`);
511
+ } else {
512
+ throw err;
513
+ }
514
+ }
515
+ await fs2.writeFile(
516
+ path.resolve(targetFolder, ".github/release-please-manifest.json"),
517
+ `{
518
+ ".": "${INITIAL_VERSION}"
519
+ }
520
+ `,
521
+ "utf8"
522
+ );
523
+ await fs2.writeFile(path.resolve(targetFolder, "CHANGELOG.md"), "# Changelog\n", "utf8");
524
+ }
475
525
 
476
526
  // src/utils/download-template.ts
477
527
  import fs3 from "fs";
@@ -490,7 +540,9 @@ async function downloadGithubTemplate(template, ref, targetFolder) {
490
540
  const { owner, repo } = parseGithubRepo(template);
491
541
  const url = `https://github.com/${owner}/${repo}.git`;
492
542
  await git2.clone({ fs: fs3, http, dir: targetFolder, url, ref, singleBranch: true, depth: 1 });
543
+ const baseSha = await git2.resolveRef({ fs: fs3, dir: targetFolder, ref: "HEAD" });
493
544
  await rm(join(targetFolder, ".git"), { recursive: true, force: true });
545
+ return baseSha;
494
546
  }
495
547
 
496
548
  // src/utils/list-template-files.ts
@@ -613,6 +665,21 @@ async function collectModuleFiles(dir, out) {
613
665
  }
614
666
  }
615
667
 
668
+ // src/utils/write-scaffold-base.ts
669
+ import { mkdir, writeFile } from "fs/promises";
670
+ import { join as join4 } from "path";
671
+ var CELLA_DIR = ".cella";
672
+ var BASE_FILE = "base";
673
+ async function writeScaffoldBase(targetFolder, sha) {
674
+ const dir = join4(targetFolder, CELLA_DIR);
675
+ await mkdir(dir, { recursive: true });
676
+ const contents = `# Upstream cella commit this project was scaffolded from (create-cella).
677
+ # Used by \`pnpm cella sync\` to bootstrap the first merge-base. Do not edit or delete.
678
+ ${sha}
679
+ `;
680
+ await writeFile(join4(dir, BASE_FILE), contents, "utf8");
681
+ }
682
+
616
683
  // src/create.ts
617
684
  function isLocalPath(path2) {
618
685
  return path2.startsWith("/") || path2.startsWith("./") || path2.startsWith("../");
@@ -636,8 +703,9 @@ async function create({
636
703
  const progress = createProgress("creating project", silent);
637
704
  try {
638
705
  progress.step("creating project folder");
639
- await mkdir(targetFolder, { recursive: true });
706
+ await mkdir2(targetFolder, { recursive: true });
640
707
  process.chdir(targetFolder);
708
+ let baseSha = null;
641
709
  if (isLocalTemplate) {
642
710
  progress.step("copying local template");
643
711
  const sourcePath = resolve2(originalCwd, templateUrl);
@@ -651,22 +719,23 @@ async function create({
651
719
  }
652
720
  const tracked = await listTemplateFiles(sourcePath);
653
721
  for (const rel2 of tracked) {
654
- const src = join4(sourcePath, rel2);
722
+ const src = join5(sourcePath, rel2);
655
723
  if (!existsSync(src)) continue;
656
- const dest = join4(targetFolder, rel2);
657
- await mkdir(dirname2(dest), { recursive: true });
724
+ const dest = join5(targetFolder, rel2);
725
+ await mkdir2(dirname2(dest), { recursive: true });
658
726
  await cp(src, dest);
659
727
  }
660
728
  } else {
661
729
  const ref = templateRef ?? "main";
662
730
  progress.step(`downloading cella template${templateRef ? ` (${templateRef})` : ""}`);
663
- await downloadGithubTemplate(template, ref, targetFolder);
731
+ baseSha = await downloadGithubTemplate(template, ref, targetFolder);
664
732
  }
665
733
  if (!silent) await promptOptionalModules(targetFolder);
666
734
  progress.step("cleaning template");
667
735
  const displayName = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
668
736
  await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });
669
- const gitFolderPath = join4(targetFolder, ".git");
737
+ if (baseSha) await writeScaffoldBase(targetFolder, baseSha);
738
+ const gitFolderPath = join5(targetFolder, ".git");
670
739
  if (!shouldSkipStep("git") && !existsSync(gitFolderPath)) {
671
740
  progress.step("initializing git");
672
741
  await gitInit(targetFolder);
@@ -711,12 +780,12 @@ async function promptOptionalModules(targetFolder) {
711
780
  confirmChoice("Optional modules", keep.length ? keep.join(", ") : pc5.dim("none"));
712
781
  resumeSpinner();
713
782
  const folders = modules.filter((m) => !keep.includes(m.name)).flatMap(optionalModuleFolders);
714
- await Promise.all(folders.map((f) => rm2(join4(targetFolder, f), { recursive: true, force: true })));
783
+ await Promise.all(folders.map((f) => rm2(join5(targetFolder, f), { recursive: true, force: true })));
715
784
  }
716
785
 
717
786
  // src/utils/detect-used-ports.ts
718
787
  import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
719
- import { dirname as dirname3, join as join5 } from "path";
788
+ import { dirname as dirname3, join as join6 } from "path";
720
789
  async function detectUsedPorts(targetFolder) {
721
790
  const parentDir = dirname3(targetFolder);
722
791
  const used = [];
@@ -727,7 +796,7 @@ async function detectUsedPorts(targetFolder) {
727
796
  return used;
728
797
  }
729
798
  for (const name of siblings) {
730
- const configPath = join5(parentDir, name, "shared/config/config.development.ts");
799
+ const configPath = join6(parentDir, name, "shared/config/config.development.ts");
731
800
  try {
732
801
  const content = await readFile3(configPath, "utf8");
733
802
  const feMatch = content.match(/frontendUrl:\s*'http:\/\/localhost:(\d+)'/);
@@ -756,17 +825,13 @@ function findNextOffset(usedPorts) {
756
825
  }
757
826
 
758
827
  // src/utils/fetch-template-metadata.ts
759
- function parseRepo(repositoryUrl) {
760
- const [owner, repo] = repositoryUrl.replace("github:", "").split("/");
761
- return [owner, repo];
762
- }
828
+ var TEMPLATE_METADATA_API_BASE = `https://api.github.com/repos/${TEMPLATE_REPOSITORY}`;
763
829
  function toDate(iso) {
764
830
  return iso ? iso.slice(0, 10) : "unknown";
765
831
  }
766
832
  var GITHUB_HEADERS = { Accept: "application/vnd.github+json" };
767
- async function fetchLatestRelease(repositoryUrl) {
768
- const [owner, repo] = parseRepo(repositoryUrl);
769
- const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases?per_page=30`;
833
+ async function fetchLatestRelease() {
834
+ const apiUrl = `${TEMPLATE_METADATA_API_BASE}/releases?per_page=30`;
770
835
  try {
771
836
  const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });
772
837
  if (!response.ok) return null;
@@ -778,9 +843,8 @@ async function fetchLatestRelease(repositoryUrl) {
778
843
  return null;
779
844
  }
780
845
  }
781
- async function fetchLatestCommit(repositoryUrl, branch = "main") {
782
- const [owner, repo] = parseRepo(repositoryUrl);
783
- const apiUrl = `https://api.github.com/repos/${owner}/${repo}/commits/${branch}`;
846
+ async function fetchLatestCommit(branch = "main") {
847
+ const apiUrl = `${TEMPLATE_METADATA_API_BASE}/commits/${branch}`;
784
848
  try {
785
849
  const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });
786
850
  if (!response.ok) return null;
@@ -831,7 +895,7 @@ async function main() {
831
895
  theme: promptTheme,
832
896
  choices: [
833
897
  { name: "Cancel and exit", value: "cancel" },
834
- { name: "Ignore existing files and continue", value: "ignore" }
898
+ { name: "Remove existing files and continue", value: "remove" }
835
899
  ]
836
900
  },
837
901
  promptContext
@@ -839,6 +903,9 @@ async function main() {
839
903
  if (action === "cancel") {
840
904
  process.exit(1);
841
905
  }
906
+ if (action === "remove") {
907
+ await rm3(targetFolder, { recursive: true, force: true });
908
+ }
842
909
  }
843
910
  const portOffset = cli.options.portOffset ?? await promptPortOffset(targetFolder, promptTheme, promptContext);
844
911
  confirmChoice("Port offset", formatOffset(portOffset));
@@ -867,7 +934,7 @@ main().catch((error) => {
867
934
  process.exit(1);
868
935
  });
869
936
  async function promptTemplateRef(theme, context) {
870
- const [release, commit] = await Promise.all([fetchLatestRelease(TEMPLATE_URL), fetchLatestCommit(TEMPLATE_URL)]);
937
+ const [release, commit] = await Promise.all([fetchLatestRelease(), fetchLatestCommit()]);
871
938
  if (!release && !commit) return void 0;
872
939
  console.info(pc6.dim("You are about to install the Cella template."));
873
940
  console.info();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-cella-cli.ts","../src/constants.ts","../package.json","../src/create.ts","../src/utils/git/command.ts","../src/add-remote.ts","../src/modules/cli/commands.ts","../src/utils/validate-project-name.ts","../src/modules/cli/display.ts","../src/utils/clean-template.ts","../src/utils/download-template.ts","../src/utils/list-template-files.ts","../src/utils/progress.ts","../src/utils/scan-optional-modules.ts","../src/utils/detect-used-ports.ts","../src/utils/fetch-template-metadata.ts","../src/utils/is-empty-directory.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { existsSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport { input, select } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { TEMPLATE_URL } from '#/constants';\nimport { create } from '#/create';\nimport { type CreateOptions, cli, confirmChoice, showWelcome } from '#/modules/cli';\nimport { detectUsedPorts, findNextOffset } from '#/utils/detect-used-ports';\nimport { fetchLatestCommit, fetchLatestRelease } from '#/utils/fetch-template-metadata';\nimport { isEmptyDirectory } from '#/utils/is-empty-directory';\nimport { validateProjectName } from '#/utils/validate-project-name';\n\nasync function main(): Promise<void> {\n // Display CLI welcome banner\n showWelcome();\n\n // Shared theme to clear prompts after answering\n const promptTheme = { prefix: '', style: { answer: (text: string) => text } };\n const promptContext = { clearPromptOnDone: true };\n\n // Choose which template snapshot to scaffold from: a stable release or the\n // latest commit. Skipped when a custom --template is provided (ref doesn't apply).\n const templateRef = cli.options.template ? undefined : await promptTemplateRef(promptTheme, promptContext);\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input(\n {\n message: 'Enter your project name',\n default: 'my-cella-app',\n theme: promptTheme,\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`;\n },\n },\n promptContext,\n );\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder)?.toLowerCase() || 'project';\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select(\n {\n message,\n theme: promptTheme,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Ignore existing files and continue', value: 'ignore' },\n ],\n },\n promptContext,\n );\n\n if (action === 'cancel') {\n process.exit(1);\n }\n }\n\n // Scan sibling directories and prompt for port offset\n const portOffset = cli.options.portOffset ?? (await promptPortOffset(targetFolder, promptTheme, promptContext));\n confirmChoice('Port offset', formatOffset(portOffset));\n\n // Prompt for admin email. Default uses the reserved `example.com` domain (RFC 2606) so the\n // seed address can never resolve or route real mail — it's a placeholder, not a real inbox.\n const adminEmail =\n cli.options.adminEmail ??\n (await input(\n {\n message: 'Admin email for initial seed user',\n default: 'admin@example.com',\n theme: promptTheme,\n },\n promptContext,\n ));\n confirmChoice('Admin email', adminEmail);\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n packageManager: cli.packageManager,\n templateUrl: cli.options.template,\n templateRef,\n portOffset,\n adminEmail,\n };\n\n await create(createOptions);\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n\n/**\n * Show a short intro with the two starting points (latest release and latest\n * commit, each with its date) and let the user choose which template snapshot to\n * scaffold from. Returns the template ref to check out (release tag or `main`), or\n * undefined to use the default branch (when metadata is unavailable).\n */\nasync function promptTemplateRef(theme: object, context: object): Promise<string | undefined> {\n const [release, commit] = await Promise.all([fetchLatestRelease(TEMPLATE_URL), fetchLatestCommit(TEMPLATE_URL)]);\n\n // No metadata (offline / rate-limited) — fall back to the default branch silently.\n if (!release && !commit) return undefined;\n\n // One-sentence intro followed by the two data points.\n console.info(pc.dim('You are about to install the Cella template.'));\n console.info();\n\n if (release) console.info(` Latest release: ${release.tag} ${pc.dim(`· ${release.date}`)}`);\n if (commit) {\n console.info(` Latest commit: ${commit.shortSha} ${pc.dim(`· ${commit.message} · ${commit.date}`)}`);\n // Clickable link to the exact commit on GitHub, in grey below the summary line.\n const repo = TEMPLATE_URL.replace('github:', '');\n console.info(` ${pc.gray(`https://github.com/${repo}/commit/${commit.sha}`)}`);\n }\n console.info();\n\n // Only one available — use it without prompting.\n if (!release) return 'main';\n if (!commit) return release.tag;\n\n // Non-interactive (CI, piped stdin, no TTY): can't render the select — default to the\n // latest release for a stable, reproducible scaffold rather than crashing the prompt.\n if (!process.stdin.isTTY) {\n confirmChoice('Latest release', release.tag);\n return release.tag;\n }\n\n const choice = await select(\n {\n message: 'Start from',\n theme,\n choices: [\n { name: 'Latest release', value: 'release' },\n { name: 'Latest commit', value: 'commit' },\n ],\n },\n context,\n );\n\n // The prompt clears itself on done, so keep the choice visible with a checkmark.\n if (choice === 'release') {\n confirmChoice('Latest release', release.tag);\n return release.tag;\n }\n // \"Latest commit\" is the tip of main; check out the branch (shallow clones can't\n // reliably fetch an arbitrary SHA over GitHub's smart-HTTP protocol).\n confirmChoice('Latest commit', commit.shortSha);\n return 'main';\n}\n\n/** Format an offset as a port overview string, e.g. \"10 → :3010 / :4010 / :5442\" */\nfunction formatOffset(o: number, suffix = ''): string {\n return `${o} → :${3000 + o} / :${4000 + o} / :${5432 + o}${suffix}`;\n}\n\n/** Scan sibling forks and prompt the user to pick a port offset */\nasync function promptPortOffset(targetFolder: string, theme: object, context: object): Promise<number> {\n const usedPorts = await detectUsedPorts(targetFolder);\n const suggested = findNextOffset(usedPorts);\n\n // Fold a compact hint into the prompt message itself so it's wiped by clearPromptOnDone\n // once an offset is chosen — no leftover \"Detected forks\" trail. Override the default\n // message style (which bolds everything) so only the \"Port offset\" title is bold.\n let message = `${pc.bold('Port offset')} ${pc.dim('· to avoid conflicts with other forks/clones')}`;\n if (usedPorts.length > 0) {\n const count = usedPorts.length;\n message += pc.dim(`\\nDetected ${count} sibling fork${count === 1 ? '' : 's'} · suggested offset ${suggested}`);\n }\n const portTheme = {\n ...(theme as Record<string, unknown>),\n style: { ...((theme as { style?: object }).style ?? {}), message: (text: string) => text },\n };\n\n // Presets exclude 0 (always offered as the explicit default below) and the suggested\n // value (offered first) to avoid duplicate choices.\n const presets = [10, 20, 30].filter((o) => o !== suggested);\n const choice = await select(\n {\n message,\n theme: portTheme,\n choices: [\n ...(suggested > 0 ? [{ name: formatOffset(suggested, ' (suggested)'), value: suggested }] : []),\n { name: formatOffset(0, ' (default)'), value: 0 },\n ...presets.map((o) => ({ name: formatOffset(o), value: o })),\n { name: 'Custom offset', value: -1 },\n ],\n },\n context,\n );\n\n if (choice !== -1) return choice;\n\n const custom = await input(\n {\n message: 'Enter custom offset (0-490, multiples of 10)',\n default: String(suggested),\n theme,\n validate: (val) => {\n const n = Number(val);\n if (Number.isNaN(n) || n < 0 || n > 490) return 'Must be between 0 and 490';\n if (n % 10 !== 0) return 'Must be a multiple of 10';\n return true;\n },\n },\n context,\n );\n return Number(custom);\n}\n","import pc from 'picocolors';\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Name of this CLI tool */\nexport const NAME = 'create cella';\n\n/** Thin line divider for console output (60 chars wide) */\nexport const DIVIDER = '─'.repeat(60);\n\n/** URL of the template repository */\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n/** URL to the repository */\nexport const CELLA_REMOTE_URL = 'git@github.com:cellajs/cella.git';\n\n/** Export details from package.json */\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const WEBSITE: string = packageJson.homepage;\n\nexport function getHeaderLine(): string {\n const leftText = `⧈ ${NAME} · ${VERSION}`;\n const left = `${pc.cyan(`⧈ ${NAME}`)} ${pc.dim(`· ${VERSION}`)}`;\n const right = pc.cyan(WEBSITE);\n const padding = Math.max(1, 60 - leftText.length - WEBSITE.length);\n return `${left}${' '.repeat(padding)}${right}`;\n}\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = ['./cli/create', './info/QUICKSTART.md'];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = ['./backend/drizzle'];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './frontend/.env.example': './frontend/.env',\n './info/QUICKSTART.md': 'README.md',\n};\n\n/**\n * Fork config template that replaces `shared/config/config.default.ts` in new forks.\n */\nexport const PLACEHOLDER_CONFIG = 'shared/config/config.template.ts';\n\n/**\n * Read a `.env.example` file and apply key=value replacements.\n * Comments and unmatched keys are preserved as-is.\n * Returns null if the file doesn't exist (caller should generate from scratch).\n */\nexport async function generateEnvFromExample(\n examplePath: string,\n replacements: Record<string, string>,\n): Promise<string | null> {\n const { readFile } = await import('node:fs/promises');\n let content: string;\n try {\n content = await readFile(examplePath, 'utf8');\n } catch {\n return null;\n }\n\n return content.replace(/^([A-Z_][A-Z0-9_]*)=(.*)$/gm, (match, key, _value) => {\n if (key in replacements) return `${key}=${replacements[key]}`;\n return match;\n });\n}\n\n/**\n * Replacement map for `backend/.env`.\n * The backend `.env` is the single source of truth for the project slug, database\n * ports (consumed by `backend/compose.yaml`), connection URLs, admin email and API port.\n */\nexport function getBackendEnvReplacements(\n slug: string,\n adminEmail: string,\n portOffset: number,\n): Record<string, string> {\n const db = 5432 + portOffset;\n return {\n PROJECT_SLUG: slug,\n DB_PORT: String(db),\n DB_TEST_PORT: String(5434 + portOffset),\n DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,\n DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,\n DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,\n ADMIN_EMAIL: adminEmail,\n PORT: String(4000 + portOffset),\n };\n}\n\n/**\n * Generate env config files with project-specific values.\n * All configs go through the same data-driven loop.\n * Values prefixed with '=' are emitted as raw TS expressions (not quoted).\n */\nexport function generateEnvConfigs(slug: string, name: string, portOffset: number): Record<string, string> {\n const fe = 3000 + portOffset;\n const be = 4000 + portOffset;\n\n const header =\n \"import type { DeepPartial } from '../src/config-builder/types';\\nimport type _default from './config.default';\\n\";\n\n // Per-environment specs: optional imports + object props (= prefix → raw TS expression)\n const envs: Record<string, { imports?: string; props: Record<string, string | boolean> }> = {\n development: {\n props: {\n slug: `${slug}-development`,\n domain: '',\n frontendUrl: `http://localhost:${fe}`,\n backendUrl: `http://localhost:${be}`,\n backendAuthUrl: `http://localhost:${be}/auth`,\n },\n },\n staging: {\n props: {\n slug: `${slug}-staging`,\n domain: `${slug}.example.com`,\n frontendUrl: `https://staging.${slug}.example.com`,\n backendUrl: `https://api-staging.${slug}.example.com`,\n backendAuthUrl: `https://api-staging.${slug}.example.com/auth`,\n },\n },\n tunnel: {\n props: {\n frontendUrl: `https://localhost:${fe}`,\n backendUrl: `https://${slug}.ngrok.dev`,\n backendAuthUrl: `https://${slug}.ngrok.dev/auth`,\n },\n },\n test: {\n imports: \"import development from './config.development';\\n\",\n props: {\n domain: '',\n frontendUrl: '=development.frontendUrl',\n backendUrl: '=development.backendUrl',\n backendAuthUrl: '=development.backendAuthUrl',\n },\n },\n production: { props: { maintenance: false } },\n };\n\n // Serialize value: '=' prefix → raw TS expression, boolean → literal, string → quoted\n const lit = (v: string | boolean) => {\n if (typeof v === 'boolean') return String(v);\n if (v.startsWith('=')) return v.slice(1);\n return `'${v}'`;\n };\n\n const result: Record<string, string> = {};\n\n for (const [mode, { imports = '', props }] of Object.entries(envs)) {\n const nameEntry = mode !== 'production' ? ` name: '${name} ${mode.toUpperCase()}',\\n` : '';\n const body = Object.entries(props)\n .map(([k, v]) => ` ${k}: ${lit(v)},`)\n .join('\\n');\n result[`./shared/config/config.${mode}.ts`] =\n `${imports}${header}\\nexport default {\\n mode: '${mode}',\\n${nameEntry}${body}\\n} satisfies DeepPartial<typeof _default>;\\n`;\n }\n\n return result;\n}\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.3.2\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \"24.x\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"index.js\",\n \"README.md\"\n ],\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/create-cella-cli.ts\",\n \"try\": \"tsx ./src/create-cella-cli.ts ../cella-try-app\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run test:release\",\n \"check\": \"pnpm ts && pnpm biome check --write .\",\n \"ts\": \"tsgo --pretty\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"test\": \"vitest run\",\n \"test:release\": \"pnpm run build && vitest run --config vitest.release.config.ts\",\n \"test:watch\": \"vitest\",\n \"prepare\": \"lefthook install\"\n },\n \"dependencies\": {\n \"@inquirer/prompts\": \"^8.5.2\",\n \"commander\": \"^15.0.0\",\n \"ignore\": \"^7.0.5\",\n \"isomorphic-git\": \"^1.38.5\",\n \"ora\": \"^9.4.1\",\n \"picocolors\": \"^1.1.1\",\n \"validate-npm-package-name\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.5.1\",\n \"@commitlint/cli\": \"^21.1.0\",\n \"@commitlint/config-conventional\": \"^21.1.0\",\n \"@evilmartians/lefthook\": \"^2.1.9\",\n \"@types/node\": \"24.10.13\",\n \"@types/validate-npm-package-name\": \"^4.0.2\",\n \"@typescript/native-preview\": \"7.0.0-dev.20260628.1\",\n \"rimraf\": \"^6.1.3\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.22.4\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.9\"\n },\n \"packageManager\": \"pnpm@11.8.0\",\n \"commitlint\": {\n \"extends\": [\n \"@commitlint/config-conventional\"\n ],\n \"rules\": {\n \"type-enum\": [\n 2,\n \"always\",\n [\n \"feat\",\n \"fix\",\n \"perf\",\n \"refactor\",\n \"revert\",\n \"docs\",\n \"chore\",\n \"build\",\n \"ci\",\n \"style\",\n \"test\"\n ]\n ]\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { cp, mkdir, rm } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { checkbox } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { addRemote } from '#/add-remote';\nimport { TEMPLATE_URL } from '#/constants';\nimport { type CreateOptions, confirmChoice, showSuccess } from '#/modules/cli';\nimport { cleanTemplate } from '#/utils/clean-template';\nimport { downloadGithubTemplate } from '#/utils/download-template';\nimport { gitAddAll, gitCommit, gitInit } from '#/utils/git';\nimport { listTemplateFiles } from '#/utils/list-template-files';\nimport { createProgress, pauseSpinner, resumeSpinner } from '#/utils/progress';\nimport { optionalModuleFolders, scanOptionalModules } from '#/utils/scan-optional-modules';\n\n/** Check if a path is a local directory */\nfunction isLocalPath(path: string): boolean {\n return path.startsWith('/') || path.startsWith('./') || path.startsWith('../');\n}\n\nfunction shouldSkipStep(name: 'git' | 'remote'): boolean {\n return process.env[`CREATE_CELLA_SKIP_${name.toUpperCase()}`] === 'true';\n}\n\nexport async function create({\n projectName,\n targetFolder,\n packageManager,\n templateUrl,\n templateRef,\n portOffset,\n adminEmail,\n silent = false,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n // Use custom template or default\n const template = templateUrl || TEMPLATE_URL;\n const isLocalTemplate = templateUrl && isLocalPath(templateUrl);\n\n const progress = createProgress('creating project', silent);\n\n try {\n // Create the target folder\n progress.step('creating project folder');\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n // Download or copy the template\n if (isLocalTemplate) {\n progress.step('copying local template');\n const sourcePath = resolve(originalCwd, templateUrl);\n\n // Check if target is inside source (would cause EINVAL). Use a path-boundary check\n // (not a string prefix) so siblings like `cella-try-app` next to `cella` aren't\n // misdetected as being inside the template.\n const rel = relative(sourcePath, targetFolder);\n const targetInsideTemplate = rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));\n if (targetInsideTemplate) {\n throw new Error(\n 'Cannot create project inside the template directory.\\n' +\n ` Run from outside: cd ~ && pnpm create @cellajs/cella ${projectName} --template ${templateUrl}`,\n );\n }\n\n // Copy only what git would track (respects .gitignore: no build output, no local .env secrets).\n const tracked = await listTemplateFiles(sourcePath);\n for (const rel of tracked) {\n const src = join(sourcePath, rel);\n if (!existsSync(src)) continue; // listed but deleted in the working tree\n const dest = join(targetFolder, rel);\n await mkdir(dirname(dest), { recursive: true });\n await cp(src, dest);\n }\n } else {\n // Pin to the chosen ref (release tag or commit SHA), else the default branch.\n const ref = templateRef ?? 'main';\n progress.step(`downloading cella template${templateRef ? ` (${templateRef})` : ''}`);\n await downloadGithubTemplate(template, ref, targetFolder);\n }\n\n // Ask which optional modules to keep, then drop deselected paths\n if (!silent) await promptOptionalModules(targetFolder);\n\n // Clean the template, generate configs\n progress.step('cleaning template');\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });\n\n // Initialize git repository\n const gitFolderPath = join(targetFolder, '.git');\n if (!shouldSkipStep('git') && !existsSync(gitFolderPath)) {\n progress.step('initializing git');\n await gitInit(targetFolder);\n await gitAddAll(targetFolder);\n await gitCommit(targetFolder, 'Initial commit');\n }\n\n // Add upstream remote\n if (!shouldSkipStep('remote')) {\n progress.step('adding upstream remote');\n await addRemote({ targetFolder, silent: true });\n }\n\n // Done\n progress.done(pc.bold(`created ${projectName}`));\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n // Rethrow so callers (CLI entry, tests) see the real failure. The CLI entry\n // turns this into a non-zero exit; swallowing it here with process.exit hid\n // the underlying error from CI and vitest.\n throw error;\n }\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n // Display final success message\n showSuccess(projectName, targetFolder, relativePath, needsCd, packageManager);\n}\n\n/**\n * Scan the downloaded template for optional modules and let the user deselect them.\n * Deselected modules have their whole folder removed before cleaning. Default keeps\n * everything (parity with previous behavior).\n */\nasync function promptOptionalModules(targetFolder: string): Promise<void> {\n const modules = await scanOptionalModules(targetFolder);\n if (modules.length === 0) return;\n\n // Non-interactive (CI, piped stdin, no TTY): can't render a checkbox — keep every module\n // rather than crashing with \"User force closed the prompt\". Matches the default-keep behavior.\n if (!process.stdin.isTTY) return;\n\n pauseSpinner();\n // Blank line to separate the preceding progress step from this prompt.\n console.info();\n const keep = await checkbox(\n {\n message: 'Optional modules to include',\n // Leading space on the radio icons adds a gap after the cursor (inquirer renders\n // `${cursor}${checkbox} ${name}` with no space between cursor and checkbox).\n theme: { icon: { checked: ` ${pc.green('◉')}`, unchecked: ' ◯' } },\n choices: modules.map((m) => ({\n name: `${m.name} ${pc.dim(`· ${m.description}`)}`,\n value: m.name,\n checked: true,\n })),\n },\n // Clear the answered prompt so we can print a compact, non-bold summary that lists\n // only the kept module names (not their descriptions).\n { clearPromptOnDone: true },\n );\n\n // Keep the choice visible after the prompt clears: plain label plus the selected\n // module names only (not their descriptions), or \"none\" when everything was deselected.\n confirmChoice('Optional modules', keep.length ? keep.join(', ') : pc.dim('none'));\n resumeSpinner();\n\n // Remove every folder owned by a deselected module. `optionalModuleFolders` lists more\n // route variants than any single module uses; `force: true` ignores the ones absent here.\n const folders = modules.filter((m) => !keep.includes(m.name)).flatMap(optionalModuleFolders);\n await Promise.all(folders.map((f) => rm(join(targetFolder, f), { recursive: true, force: true })));\n}\n","import fs from 'node:fs';\nimport git from 'isomorphic-git';\n\n/**\n * Git helpers backed by isomorphic-git (pure JS) instead of shelling out to the\n * `git` binary. This keeps `create-cella` free of `child_process` (no shell access)\n * and removes any dependency on the user's global git config — the initial commit\n * uses an explicit author, so scaffolding never fails with \"Author identity unknown\".\n */\n\n/** Author used for the scaffold's initial commit (users re-author their own commits later). */\nconst INITIAL_AUTHOR = { name: 'cella', email: 'info@cellajs.com' } as const;\n\n/**\n * Initializes a new Git repository in the specified directory.\n */\nexport async function gitInit(repoPath: string): Promise<void> {\n await git.init({ fs, dir: repoPath, defaultBranch: 'main' });\n}\n\n/**\n * Stages all files in the repository (respects `.gitignore`).\n */\nexport async function gitAddAll(repoPath: string): Promise<void> {\n await git.add({ fs, dir: repoPath, filepath: '.' });\n}\n\n/**\n * Creates a commit with the specified message and returns the commit SHA.\n */\nexport async function gitCommit(repoPath: string, message: string): Promise<string> {\n return git.commit({ fs, dir: repoPath, message, author: INITIAL_AUTHOR });\n}\n\n/**\n * Gets the URL of a remote. Throws when the remote does not exist (mirrors\n * `git remote get-url` failing) so callers can detect a missing remote.\n */\nexport async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {\n const remotes = await git.listRemotes({ fs, dir: repoPath });\n const match = remotes.find((r) => r.remote === remoteName);\n if (!match) throw new Error(`No such remote '${remoteName}'`);\n return match.url;\n}\n\n/**\n * Adds a new remote.\n */\nexport async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<void> {\n await git.addRemote({ fs, dir: repoPath, remote: remoteName, url: remoteUrl });\n}\n\n/**\n * Removes a remote.\n */\nexport async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<void> {\n await git.deleteRemote({ fs, dir: repoPath, remote: remoteName });\n}\n","import { CELLA_REMOTE_URL } from '#/constants';\nimport type { AddRemoteOptions } from '#/modules/cli';\nimport { gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove } from '#/utils/git';\n\n/**\n * Adds or updates the upstream remote for the Cella template.\n * @param options - Configuration options\n * @param options.silent - If true, don't throw on failure (used when called from progress tracker)\n */\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = 'upstream',\n silent = false,\n}: AddRemoteOptions): Promise<void> {\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await gitRemoteGetUrl(targetFolder, remoteName);\n } catch {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await gitRemoteRemove(targetFolder, remoteName);\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n }\n } catch (error) {\n if (!silent) {\n throw error;\n }\n }\n}\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { NAME, VERSION } from '#/constants';\nimport { validateProjectName } from '#/utils/validate-project-name';\nimport type { CLIConfig, CLIOptions } from './types';\n\nfunction parsePortOffset(value: string): number {\n const parsed = Number(value);\n\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > 490) {\n throw new InvalidArgumentError('Port offset must be an integer between 0 and 490');\n }\n\n if (parsed % 10 !== 0) {\n throw new InvalidArgumentError('Port offset must be a multiple of 10');\n }\n\n return parsed;\n}\n\n// Initialize CLI variables\nlet directory: string | null = null;\nconst packageManager = 'pnpm';\n\n/**\n * Defines the root CLI command using Commander.\n * This command accepts CLI options and validates user input.\n */\nexport const command = new Command(NAME)\n .version(VERSION, '-v, --version', `output the current version of ${NAME}`)\n .argument('[directory]', 'the directory name for the new project')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'display this help message')\n .option('--template <path>', 'use a custom template (local path or github:user/repo)')\n .option('--port-offset <number>', 'set the port offset (0-490 in steps of 10)', parsePortOffset)\n .option('--admin-email <email>', 'set the admin email for the initial seed user')\n .action((name: string) => {\n const trimmedName = typeof name === 'string' ? name.trim() : name;\n\n if (trimmedName) {\n const validation = validateProjectName(basename(resolve(trimmedName)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(`Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`);\n }\n\n directory = trimmedName;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>();\n\n/**\n * Runs the CLI and returns the parsed configuration.\n * This function parses command line arguments and returns the CLI config.\n */\nexport function runCli(): CLIConfig {\n return {\n options,\n args: command.args,\n directory,\n packageManager,\n };\n}\n\n// Export CLI configuration for direct import\nexport const cli: CLIConfig = runCli();\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],\n };\n}\n","import pc from 'picocolors';\nimport { DESCRIPTION, DIVIDER, getHeaderLine } from '#/constants';\n\n/** ASCII art logo for the CLI welcome screen. */\nfunction showAscii(): void {\n console.info(pc.cyan(' _ _ '));\n console.info(pc.cyan('▒▓█████▓▒ ___ ___| | | __ _ '));\n console.info(pc.cyan('▒▓█ █▓▒ / __/ _ \\\\ | |/ _` | '));\n console.info(pc.cyan('▒▓█ █▓▒ | (_| __/ | | (_| | '));\n console.info(pc.cyan('▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_| '));\n}\n\n/**\n * Displays the compact CLI welcome header.\n */\nexport function showWelcome(): void {\n console.info();\n showAscii();\n console.info();\n console.info(pc.dim(DESCRIPTION));\n console.info();\n console.info(getHeaderLine());\n console.info(DIVIDER);\n}\n\n/**\n * Print a cleared prompt's outcome as a persistent confirmation line:\n * `✓ <label> · <value>`, followed by a blank separator line. Used after prompts\n * that clear themselves on done so the chosen value stays visible.\n */\nexport function confirmChoice(label: string, value: string): void {\n console.info(`${pc.green('✓')} ${label} · ${value}`);\n console.info();\n}\n\n/**\n * Displays the final success message after project creation.\n */\nexport function showSuccess(\n projectName: string,\n _targetFolder: string,\n relativePath: string,\n needsCd: boolean,\n packageManager: string,\n): void {\n console.info();\n\n console.info(pc.bold('Next steps'));\n console.info(DIVIDER);\n console.info();\n\n if (needsCd) {\n console.info(`${pc.dim('$')} cd ${pc.cyan(relativePath)}`);\n }\n console.info(`${pc.dim('$')} ${pc.cyan(`${packageManager} install`)}`);\n console.info(`${pc.dim('$')} ${pc.cyan(`${packageManager} generate`)}`);\n console.info(\n `${pc.dim('$')} ${pc.cyan(`${packageManager} docker`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} seed`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} dev`)}`,\n );\n console.info();\n\n // Point to the generated README for first-run details and sign-in credentials. Use the\n // path relative to the new project so the terminal linkifies the right file (not this CLI's).\n const readmePath = relativePath ? `${relativePath}/README.md` : 'README.md';\n console.info(`${pc.gray('To get started building, please read:')} ${pc.cyan(readmePath)}`);\n console.info();\n console.info(`🥳 enjoy building ${pc.green(projectName)} with cella!`);\n console.info();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport pc from 'picocolors';\nimport {\n generateEnvConfigs,\n generateEnvFromExample,\n getBackendEnvReplacements,\n PLACEHOLDER_CONFIG,\n TO_CLEAN,\n TO_COPY,\n TO_REMOVE,\n} from '#/constants';\n\nconst warningMark = pc.yellow('⚠');\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder, project name, and optional extra edits.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n displayName,\n portOffset = 0,\n adminEmail = `admin@${projectName}.example.com`,\n}: {\n targetFolder: string;\n projectName: string;\n displayName: string;\n portOffset?: number;\n adminEmail?: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>((resolve, reject) => {\n (async () => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Replace config.default.ts with interpolated placeholder config\n await applyPlaceholderConfig(targetFolder, projectName, displayName);\n\n // Generate backend .env from backend/.env.example.\n // The backend .env is the single source of truth for the project slug and DB ports\n // (consumed by backend/compose.yaml). There is no root .env.\n const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);\n const backendEnv = await generateEnvFromExample(\n path.resolve(targetFolder, 'backend/.env.example'),\n backendReplacements,\n );\n if (backendEnv) {\n await fs.writeFile(path.resolve(targetFolder, 'backend/.env'), backendEnv, 'utf8');\n }\n\n // Generate minimal env config files with project values and ports baked in\n const envConfigs = generateEnvConfigs(projectName, displayName, portOffset);\n await Promise.all(\n Object.entries(envConfigs).map(([filePath, content]) =>\n fs.writeFile(path.resolve(targetFolder, filePath), content, 'utf8'),\n ),\n );\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n }),\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n }),\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n })();\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nasync function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder. Skip silently if the folder doesn't exist —\n // the template may not include every optional folder (e.g. backend/drizzle).\n let files: string[];\n try {\n files = await fs.readdir(folderPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n\n // `recursive: true` handles nested folders; `force: true` ignores races/missing entries.\n await Promise.all(files.map((file) => fs.rm(path.join(folderPath, file), { recursive: true, force: true })));\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nasync function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nasync function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Read the fork config template shipped inside the cloned Cella template\n * (`shared/config/config.template.ts`), interpolate project tokens, write the\n * result as `shared/config/config.default.ts` — replacing the original — and\n * delete the now-consumed template from the fork.\n */\nasync function applyPlaceholderConfig(targetFolder: string, projectName: string, displayName: string): Promise<void> {\n const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);\n const dest = path.resolve(targetFolder, './shared/config/config.default.ts');\n\n try {\n let content = await fs.readFile(src, 'utf8');\n content = content.replaceAll('__project_name__', displayName);\n content = content.replaceAll('__project_slug__', projectName);\n await fs.writeFile(dest, content, 'utf8');\n await fs.rm(src, { force: true });\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Fork config template \"${src}\" not found > Skip`);\n } else {\n throw err;\n }\n }\n}\n","import fs from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport git from 'isomorphic-git';\nimport http from 'isomorphic-git/http/node';\n\n/** Parse `github:owner/repo` or `owner/repo` into its owner and repo parts. */\nfunction parseGithubRepo(template: string): { owner: string; repo: string } {\n const [owner, repo] = template.replace(/^github:/, '').split('/');\n if (!owner || !repo) {\n throw new Error(`Invalid template '${template}'. Expected 'github:owner/repo'.`);\n }\n return { owner, repo };\n}\n\n/**\n * Clones a GitHub template into `targetFolder` using isomorphic-git (pure JS).\n *\n * A shallow, single-branch clone over HTTPS — no `git` binary, no `child_process`,\n * so `create-cella` keeps a clean supply-chain profile (no shell access). Unlike a\n * raw tarball parse, isomorphic-git reconstructs the tree itself, so file modes and\n * long paths are always correct. The cloned `.git` history is discarded afterwards;\n * scaffolding re-initialises the repo with a fresh initial commit.\n *\n * @param template - `github:owner/repo` (or `owner/repo`).\n * @param ref - Branch or tag to check out (e.g. `main` or a release tag).\n * @param targetFolder - Directory to clone the template into.\n */\nexport async function downloadGithubTemplate(template: string, ref: string, targetFolder: string): Promise<void> {\n const { owner, repo } = parseGithubRepo(template);\n const url = `https://github.com/${owner}/${repo}.git`;\n\n await git.clone({ fs, http, dir: targetFolder, url, ref, singleBranch: true, depth: 1 });\n\n // Drop the cloned history — the scaffold gets its own fresh initial commit.\n await rm(join(targetFolder, '.git'), { recursive: true, force: true });\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ignore from 'ignore';\n\n/**\n * Lists the files under `root` that git would track, without shelling out to git.\n *\n * Honors the root `.gitignore` (via the `ignore` package) and always skips `.git`\n * and `node_modules`. Used for the local-template dev flow (`--template ./local`)\n * so build output and local `.env` secrets aren't copied into the new project.\n *\n * Paths are returned relative to `root` using POSIX separators.\n */\nexport async function listTemplateFiles(root: string): Promise<string[]> {\n const ig = ignore().add(['.git', 'node_modules']);\n try {\n ig.add(await readFile(join(root, '.gitignore'), 'utf8'));\n } catch {\n // No .gitignore at the template root — only the defaults above apply.\n }\n\n const files: string[] = [];\n\n async function walk(relDir: string): Promise<void> {\n const entries = await readdir(relDir ? join(root, relDir) : root, { withFileTypes: true });\n for (const entry of entries) {\n const rel = relDir ? `${relDir}/${entry.name}` : entry.name;\n // `ignore` matches directory rules when the path ends with a slash.\n if (ig.ignores(entry.isDirectory() ? `${rel}/` : rel)) continue;\n if (entry.isDirectory()) await walk(rel);\n else if (entry.isFile()) files.push(rel);\n }\n }\n\n await walk('');\n return files;\n}\n","/**\n * Progress Tracker Utility\n *\n * Provides compact progress tracking for CLI operations.\n * Uses a single spinner that updates in place.\n */\n\nimport ora, { type Ora } from 'ora';\nimport pc from 'picocolors';\n\n/** Global reference to the currently active spinner */\nlet activeSpinner: Ora | null = null;\n\n/** Pause the active spinner (for interactive prompts) */\nexport function pauseSpinner(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n}\n\n/** Resume the active spinner after a prompt */\nexport function resumeSpinner(): void {\n if (activeSpinner) {\n activeSpinner.start();\n }\n}\n\nexport interface ProgressTracker {\n /** Update the current step (shown as spinner text) */\n step: (message: string) => void;\n /** Mark the tracker as complete with final message */\n done: (message: string) => void;\n /** Mark the tracker as failed with error message */\n fail: (message: string) => void;\n}\n\n/**\n * Creates a progress tracker for a multi-step operation.\n * Shows a single spinner that updates in place.\n *\n * @param title - The initial title for the spinner\n * @returns A ProgressTracker instance\n *\n * @example\n * const progress = createProgress('creating project');\n * progress.step('downloading template');\n * progress.step('installing dependencies');\n * progress.done('project created');\n */\nexport function createProgress(title: string, silent = false): ProgressTracker {\n const completedSteps: string[] = [];\n\n const spinner = ora({\n text: title,\n isSilent: silent,\n });\n\n activeSpinner = spinner;\n spinner.start();\n\n // Helper to log (respects silent mode)\n const log = (msg: string) => {\n if (!silent) console.info(msg);\n };\n\n return {\n step: (message: string) => {\n // Complete previous step with green check if there was one\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n }\n completedSteps.push(message);\n spinner.text = message;\n spinner.start();\n },\n\n done: (message: string) => {\n // Complete the last step with green check\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n } else {\n spinner.stop();\n }\n activeSpinner = null;\n if (message) {\n log('');\n log(`${pc.green('✓')} ${message}`);\n }\n },\n\n fail: (message: string) => {\n spinner.stop();\n activeSpinner = null;\n log(`${pc.red('✗')} ${pc.red(message)}`);\n },\n };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\n\n/** An optional module discovered in a `*-module.ts` registration. */\nexport interface OptionalModule {\n name: string;\n description: string;\n /** Module folder, relative to the project root, removed when deselected. */\n folder: string;\n}\n\n/** Directories never worth scanning. */\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.turbo', 'coverage']);\n\n/**\n * Scan the template for `*-module.ts` files that register `optional: true` and return\n * their name, description and folder. Pure static regex parsing — no imports/eval — so it\n * runs before dependencies are installed. The folder is the module file's directory.\n */\nexport async function scanOptionalModules(targetFolder: string): Promise<OptionalModule[]> {\n const files: string[] = [];\n await collectModuleFiles(targetFolder, files);\n\n const modules: OptionalModule[] = [];\n for (const file of files) {\n const src = await readFile(file, 'utf8');\n if (!/optional:\\s*true/.test(src)) continue;\n\n const name = src.match(/name:\\s*['\"]([^'\"]+)['\"]/)?.[1];\n const description = src.match(/description:\\s*['\"]([^'\"]+)['\"]/)?.[1] ?? '';\n if (!name) continue;\n\n modules.push({ name, description, folder: relative(targetFolder, dirname(file)) });\n }\n return modules.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Every project-root-relative folder removed when an optional module is deselected: its own\n * module folder plus the route and static-asset folders it may own. Route folders are\n * speculative — a module owns either a pathless `_<name>` (URL-transparent) or a path-based\n * `<name>` route, under `routes/_public` and/or `routes/_app` — so callers remove only the\n * variants that actually exist. Single-sourced here so removal and tests stay in sync.\n */\nexport function optionalModuleFolders({ name, folder }: OptionalModule): string[] {\n return [\n folder,\n `frontend/src/routes/_public/_${name}`,\n `frontend/src/routes/_public/${name}`,\n `frontend/src/routes/_app/_${name}`,\n `frontend/src/routes/_app/${name}`,\n `frontend/public/static/${name}`,\n ];\n}\n\nasync function collectModuleFiles(dir: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await collectModuleFiles(join(dir, entry.name), out);\n } else if (entry.name.endsWith('-module.ts')) {\n out.push(join(dir, entry.name));\n }\n }\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\ninterface UsedPorts {\n project: string;\n frontend: number;\n backend: number;\n offset: number;\n}\n\n/**\n * Scan sibling directories for existing cella forks and extract their dev ports.\n * Looks for `shared/config/config.development.ts` to identify cella-based projects.\n */\nexport async function detectUsedPorts(targetFolder: string): Promise<UsedPorts[]> {\n const parentDir = dirname(targetFolder);\n const used: UsedPorts[] = [];\n\n let siblings: string[];\n try {\n siblings = await readdir(parentDir);\n } catch {\n return used;\n }\n\n for (const name of siblings) {\n const configPath = join(parentDir, name, 'shared/config/config.development.ts');\n try {\n const content = await readFile(configPath, 'utf8');\n const feMatch = content.match(/frontendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n const beMatch = content.match(/backendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n if (feMatch && beMatch) {\n const frontend = Number(feMatch[1]);\n const backend = Number(beMatch[1]);\n used.push({\n project: name,\n frontend,\n backend,\n offset: frontend - 3000,\n });\n }\n } catch {\n // Not a cella fork, skip\n }\n }\n\n return used;\n}\n\n/** Find the next available offset (in steps of 10) that doesn't collide with existing forks. */\nexport function findNextOffset(usedPorts: UsedPorts[]): number {\n const usedOffsets = new Set(usedPorts.map((p) => p.offset));\n for (let offset = 0; offset <= 490; offset += 10) {\n if (!usedOffsets.has(offset)) return offset;\n }\n return 0;\n}\n","/**\n * Fetch lightweight metadata about the cella template from the GitHub API so the\n * scaffolder can offer \"latest release\" vs \"latest commit\" as a starting point.\n *\n * Both helpers fail soft (return null) on network/API errors or rate limits — the\n * caller falls back to the default branch when metadata is unavailable.\n */\n\n/** Parse 'github:owner/repo' (or 'owner/repo') into [owner, repo]. */\nfunction parseRepo(repositoryUrl: string): [string, string] {\n const [owner, repo] = repositoryUrl.replace('github:', '').split('/');\n return [owner, repo];\n}\n\n/** Format an ISO timestamp as a locale-independent YYYY-MM-DD date. */\nfunction toDate(iso?: string): string {\n return iso ? iso.slice(0, 10) : 'unknown';\n}\n\nconst GITHUB_HEADERS = { Accept: 'application/vnd.github+json' } as const;\n\nexport interface ReleaseInfo {\n /** Release tag, e.g. 'v0.5.0' */\n tag: string;\n /** Publication date, YYYY-MM-DD */\n date: string;\n}\n\nexport interface CommitInfo {\n /** Full commit SHA (used for a reproducible checkout) */\n sha: string;\n /** Short 7-char SHA for display */\n shortSha: string;\n /** First line of the commit message */\n message: string;\n /** Commit date, YYYY-MM-DD */\n date: string;\n}\n\n/**\n * Fetch the latest published template release (`v*` tag).\n *\n * cella publishes two release series — the template (`v*`) and the scaffolder\n * (`create-cella-v*`). We list releases (newest first) and pick the first\n * non-draft, non-prerelease `v*` release so the scaffolder version is ignored.\n */\nexport async function fetchLatestRelease(repositoryUrl: string): Promise<ReleaseInfo | null> {\n const [owner, repo] = parseRepo(repositoryUrl);\n const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases?per_page=30`;\n\n try {\n const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });\n if (!response.ok) return null;\n\n const releases = (await response.json()) as Array<{\n tag_name?: string;\n published_at?: string;\n draft?: boolean;\n prerelease?: boolean;\n }>;\n\n const release = releases.find((r) => !r.draft && !r.prerelease && /^v?\\d/.test(r.tag_name ?? ''));\n if (!release?.tag_name) return null;\n\n return { tag: release.tag_name, date: toDate(release.published_at) };\n } catch {\n return null;\n }\n}\n\n/** Fetch the latest commit on a branch (defaults to 'main'). */\nexport async function fetchLatestCommit(repositoryUrl: string, branch = 'main'): Promise<CommitInfo | null> {\n const [owner, repo] = parseRepo(repositoryUrl);\n const apiUrl = `https://api.github.com/repos/${owner}/${repo}/commits/${branch}`;\n\n try {\n const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n sha?: string;\n commit?: { message?: string; author?: { date?: string } };\n };\n if (!data.sha) return null;\n\n const message = (data.commit?.message ?? '').split('\\n')[0];\n return { sha: data.sha, shortSha: data.sha.slice(0, 7), message, date: toDate(data.commit?.author?.date) };\n } catch {\n return null;\n }\n}\n","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n *\n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n"],"mappings":"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","names":["existsSync","basename","resolve","pc","readFile","rm","dirname","join","relative","resolve","pc","pc","pc","packageManager","fs","pc","pc","resolve","fs","fs","git","join","pc","readdir","readFile","join","path","packageManager","resolve","relative","rel","join","dirname","pc","rm","readdir","readFile","dirname","join","readdir","path","basename","resolve","existsSync","pc"]}
1
+ {"version":3,"sources":["../src/create-cella-cli.ts","../src/constants.ts","../package.json","../src/create.ts","../src/utils/git/command.ts","../src/add-remote.ts","../src/modules/cli/commands.ts","../src/utils/validate-project-name.ts","../src/modules/cli/display.ts","../src/utils/clean-template.ts","../src/utils/download-template.ts","../src/utils/list-template-files.ts","../src/utils/progress.ts","../src/utils/scan-optional-modules.ts","../src/utils/write-scaffold-base.ts","../src/utils/detect-used-ports.ts","../src/utils/fetch-template-metadata.ts","../src/utils/is-empty-directory.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { existsSync } from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\nimport { input, select } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { TEMPLATE_URL } from '#/constants';\nimport { create } from '#/create';\nimport { type CreateOptions, cli, confirmChoice, showWelcome } from '#/modules/cli';\nimport { detectUsedPorts, findNextOffset } from '#/utils/detect-used-ports';\nimport { fetchLatestCommit, fetchLatestRelease } from '#/utils/fetch-template-metadata';\nimport { isEmptyDirectory } from '#/utils/is-empty-directory';\nimport { validateProjectName } from '#/utils/validate-project-name';\n\nasync function main(): Promise<void> {\n // Display CLI welcome banner\n showWelcome();\n\n // Shared theme to clear prompts after answering\n const promptTheme = { prefix: '', style: { answer: (text: string) => text } };\n const promptContext = { clearPromptOnDone: true };\n\n // Choose which template snapshot to scaffold from: a stable release or the\n // latest commit. Skipped when a custom --template is provided (ref doesn't apply).\n const templateRef = cli.options.template ? undefined : await promptTemplateRef(promptTheme, promptContext);\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input(\n {\n message: 'Enter your project name',\n default: 'my-cella-app',\n theme: promptTheme,\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`;\n },\n },\n promptContext,\n );\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder)?.toLowerCase() || 'project';\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select(\n {\n message,\n theme: promptTheme,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Remove existing files and continue', value: 'remove' },\n ],\n },\n promptContext,\n );\n\n if (action === 'cancel') {\n process.exit(1);\n }\n\n // Wipe the folder so the template clone starts from a clean slate. Leaving stale\n // files behind makes the git checkout fail with conflicts on overlapping paths.\n if (action === 'remove') {\n await rm(targetFolder, { recursive: true, force: true });\n }\n }\n\n // Scan sibling directories and prompt for port offset\n const portOffset = cli.options.portOffset ?? (await promptPortOffset(targetFolder, promptTheme, promptContext));\n confirmChoice('Port offset', formatOffset(portOffset));\n\n // Prompt for admin email. Default uses the reserved `example.com` domain (RFC 2606) so the\n // seed address can never resolve or route real mail — it's a placeholder, not a real inbox.\n const adminEmail =\n cli.options.adminEmail ??\n (await input(\n {\n message: 'Admin email for initial seed user',\n default: 'admin@example.com',\n theme: promptTheme,\n },\n promptContext,\n ));\n confirmChoice('Admin email', adminEmail);\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n packageManager: cli.packageManager,\n templateUrl: cli.options.template,\n templateRef,\n portOffset,\n adminEmail,\n };\n\n await create(createOptions);\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n\n/**\n * Show a short intro with the two starting points (latest release and latest\n * commit, each with its date) and let the user choose which template snapshot to\n * scaffold from. Returns the template ref to check out (release tag or `main`), or\n * undefined to use the default branch (when metadata is unavailable).\n */\nasync function promptTemplateRef(theme: object, context: object): Promise<string | undefined> {\n const [release, commit] = await Promise.all([fetchLatestRelease(), fetchLatestCommit()]);\n\n // No metadata (offline / rate-limited) — fall back to the default branch silently.\n if (!release && !commit) return undefined;\n\n // One-sentence intro followed by the two data points.\n console.info(pc.dim('You are about to install the Cella template.'));\n console.info();\n\n if (release) console.info(` Latest release: ${release.tag} ${pc.dim(`· ${release.date}`)}`);\n if (commit) {\n console.info(` Latest commit: ${commit.shortSha} ${pc.dim(`· ${commit.message} · ${commit.date}`)}`);\n // Clickable link to the exact commit on GitHub, in grey below the summary line.\n const repo = TEMPLATE_URL.replace('github:', '');\n console.info(` ${pc.gray(`https://github.com/${repo}/commit/${commit.sha}`)}`);\n }\n console.info();\n\n // Only one available — use it without prompting.\n if (!release) return 'main';\n if (!commit) return release.tag;\n\n // Non-interactive (CI, piped stdin, no TTY): can't render the select — default to the\n // latest release for a stable, reproducible scaffold rather than crashing the prompt.\n if (!process.stdin.isTTY) {\n confirmChoice('Latest release', release.tag);\n return release.tag;\n }\n\n const choice = await select(\n {\n message: 'Start from',\n theme,\n choices: [\n { name: 'Latest release', value: 'release' },\n { name: 'Latest commit', value: 'commit' },\n ],\n },\n context,\n );\n\n // The prompt clears itself on done, so keep the choice visible with a checkmark.\n if (choice === 'release') {\n confirmChoice('Latest release', release.tag);\n return release.tag;\n }\n // \"Latest commit\" is the tip of main; check out the branch (shallow clones can't\n // reliably fetch an arbitrary SHA over GitHub's smart-HTTP protocol).\n confirmChoice('Latest commit', commit.shortSha);\n return 'main';\n}\n\n/** Format an offset as a port overview string, e.g. \"10 → :3010 / :4010 / :5442\" */\nfunction formatOffset(o: number, suffix = ''): string {\n return `${o} → :${3000 + o} / :${4000 + o} / :${5432 + o}${suffix}`;\n}\n\n/** Scan sibling forks and prompt the user to pick a port offset */\nasync function promptPortOffset(targetFolder: string, theme: object, context: object): Promise<number> {\n const usedPorts = await detectUsedPorts(targetFolder);\n const suggested = findNextOffset(usedPorts);\n\n // Fold a compact hint into the prompt message itself so it's wiped by clearPromptOnDone\n // once an offset is chosen — no leftover \"Detected forks\" trail. Override the default\n // message style (which bolds everything) so only the \"Port offset\" title is bold.\n let message = `${pc.bold('Port offset')} ${pc.dim('· to avoid conflicts with other forks/clones')}`;\n if (usedPorts.length > 0) {\n const count = usedPorts.length;\n message += pc.dim(`\\nDetected ${count} sibling fork${count === 1 ? '' : 's'} · suggested offset ${suggested}`);\n }\n const portTheme = {\n ...(theme as Record<string, unknown>),\n style: { ...((theme as { style?: object }).style ?? {}), message: (text: string) => text },\n };\n\n // Presets exclude 0 (always offered as the explicit default below) and the suggested\n // value (offered first) to avoid duplicate choices.\n const presets = [10, 20, 30].filter((o) => o !== suggested);\n const choice = await select(\n {\n message,\n theme: portTheme,\n choices: [\n ...(suggested > 0 ? [{ name: formatOffset(suggested, ' (suggested)'), value: suggested }] : []),\n { name: formatOffset(0, ' (default)'), value: 0 },\n ...presets.map((o) => ({ name: formatOffset(o), value: o })),\n { name: 'Custom offset', value: -1 },\n ],\n },\n context,\n );\n\n if (choice !== -1) return choice;\n\n const custom = await input(\n {\n message: 'Enter custom offset (0-490, multiples of 10)',\n default: String(suggested),\n theme,\n validate: (val) => {\n const n = Number(val);\n if (Number.isNaN(n) || n < 0 || n > 490) return 'Must be between 0 and 490';\n if (n % 10 !== 0) return 'Must be a multiple of 10';\n return true;\n },\n },\n context,\n );\n return Number(custom);\n}\n","import pc from 'picocolors';\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Name of this CLI tool */\nexport const NAME = 'create cella';\n\n/** Thin line divider for console output (60 chars wide) */\nexport const DIVIDER = '─'.repeat(60);\n\n/** URL of the template repository */\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n/** Canonical owner/repo for template metadata fetched from the GitHub API. */\nexport const TEMPLATE_REPOSITORY = 'cellajs/cella';\n\n/** URL to the repository */\nexport const CELLA_REMOTE_URL = 'git@github.com:cellajs/cella.git';\n\n/**\n * Git remote name pointing at upstream cella. Matches the name the sync CLI\n * (`pnpm cella`) expects, so scaffolds don't end up with a duplicate remote.\n */\nexport const CELLA_REMOTE_NAME = 'cella-upstream';\n\n/** Export details from package.json */\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const WEBSITE: string = packageJson.homepage;\n\nexport function getHeaderLine(): string {\n const leftText = `⧈ ${NAME} · ${VERSION}`;\n const left = `${pc.cyan(`⧈ ${NAME}`)} ${pc.dim(`· ${VERSION}`)}`;\n const right = pc.cyan(WEBSITE);\n const padding = Math.max(1, 60 - leftText.length - WEBSITE.length);\n return `${left}${' '.repeat(padding)}${right}`;\n}\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = ['./infra/Pulumi.production.yaml'];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = ['./backend/drizzle'];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './frontend/.env.example': './frontend/.env',\n './cella/QUICKSTART.md': 'README.md',\n};\n\n/**\n * Fork config template that replaces `shared/config/config.default.ts` in new forks.\n */\nexport const PLACEHOLDER_CONFIG = 'shared/config/config.template.ts';\n\n/**\n * Starting version for a fresh fork. Resets the upstream cella version so\n * release-please cuts the fork's first release from a clean slate.\n */\nexport const INITIAL_VERSION = '0.0.0';\n\n/**\n * Read a `.env.example` file and apply key=value replacements.\n * Comments and unmatched keys are preserved as-is.\n * Returns null if the file doesn't exist (caller should generate from scratch).\n */\nexport async function generateEnvFromExample(\n examplePath: string,\n replacements: Record<string, string>,\n): Promise<string | null> {\n const { readFile } = await import('node:fs/promises');\n let content: string;\n try {\n content = await readFile(examplePath, 'utf8');\n } catch {\n return null;\n }\n\n return content.replace(/^([A-Z_][A-Z0-9_]*)=(.*)$/gm, (match, key, _value) => {\n if (key in replacements) return `${key}=${replacements[key]}`;\n return match;\n });\n}\n\n/**\n * Replacement map for `backend/.env`.\n * The backend `.env` is the single source of truth for the project slug, database\n * ports (consumed by `backend/compose.yaml`), connection URLs, admin email and API port.\n */\nexport function getBackendEnvReplacements(\n slug: string,\n adminEmail: string,\n portOffset: number,\n): Record<string, string> {\n const db = 5432 + portOffset;\n return {\n PROJECT_SLUG: slug,\n DB_PORT: String(db),\n DB_TEST_PORT: String(5434 + portOffset),\n DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,\n DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,\n DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,\n ADMIN_EMAIL: adminEmail,\n PORT: String(4000 + portOffset),\n };\n}\n\n/**\n * Generate env config files with project-specific values.\n * All configs go through the same data-driven loop.\n * Values prefixed with '=' are emitted as raw TS expressions (not quoted).\n */\nexport function generateEnvConfigs(slug: string, name: string, portOffset: number): Record<string, string> {\n const fe = 3000 + portOffset;\n const be = 4000 + portOffset;\n\n const header =\n \"import type { DeepPartial } from '../src/config-builder/types';\\nimport type _default from './config.default';\\n\";\n\n // Per-environment specs: optional imports + object props (= prefix → raw TS expression)\n const envs: Record<string, { imports?: string; props: Record<string, string | boolean> }> = {\n development: {\n props: {\n slug: `${slug}-development`,\n domain: '',\n frontendUrl: `http://localhost:${fe}`,\n backendUrl: `http://localhost:${be}`,\n backendAuthUrl: `http://localhost:${be}/auth`,\n },\n },\n staging: {\n props: {\n slug: `${slug}-staging`,\n domain: `${slug}.example.com`,\n frontendUrl: `https://staging.${slug}.example.com`,\n backendUrl: `https://api-staging.${slug}.example.com`,\n backendAuthUrl: `https://api-staging.${slug}.example.com/auth`,\n },\n },\n tunnel: {\n props: {\n frontendUrl: `https://localhost:${fe}`,\n backendUrl: `https://${slug}.ngrok.dev`,\n backendAuthUrl: `https://${slug}.ngrok.dev/auth`,\n },\n },\n test: {\n imports: \"import development from './config.development';\\n\",\n props: {\n domain: '',\n frontendUrl: '=development.frontendUrl',\n backendUrl: '=development.backendUrl',\n backendAuthUrl: '=development.backendAuthUrl',\n },\n },\n production: { props: { maintenance: false } },\n };\n\n // Serialize value: '=' prefix → raw TS expression, boolean → literal, string → quoted\n const lit = (v: string | boolean) => {\n if (typeof v === 'boolean') return String(v);\n if (v.startsWith('=')) return v.slice(1);\n return `'${v}'`;\n };\n\n const result: Record<string, string> = {};\n\n for (const [mode, { imports = '', props }] of Object.entries(envs)) {\n const nameEntry = mode !== 'production' ? ` name: '${name} ${mode.toUpperCase()}',\\n` : '';\n const body = Object.entries(props)\n .map(([k, v]) => ` ${k}: ${lit(v)},`)\n .join('\\n');\n result[`./shared/config/config.${mode}.ts`] =\n `${imports}${header}\\nexport default {\\n mode: '${mode}',\\n${nameEntry}${body}\\n} satisfies DeepPartial<typeof _default>;\\n`;\n }\n\n return result;\n}\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.3.4\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \"24.x\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"index.js\",\n \"README.md\"\n ],\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/create-cella-cli.ts\",\n \"try\": \"tsx ./src/create-cella-cli.ts ../cella-try-app\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run test:release\",\n \"check\": \"pnpm ts && pnpm biome check --write .\",\n \"ts\": \"tsgo --pretty\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"test\": \"vitest run\",\n \"test:release\": \"pnpm run build && vitest run --config vitest.release.config.ts\",\n \"test:watch\": \"vitest\",\n \"prepare\": \"lefthook install\"\n },\n \"dependencies\": {\n \"@inquirer/prompts\": \"^8.5.2\",\n \"commander\": \"^15.0.0\",\n \"ignore\": \"^7.0.5\",\n \"isomorphic-git\": \"^1.38.5\",\n \"ora\": \"^9.4.1\",\n \"picocolors\": \"^1.1.1\",\n \"validate-npm-package-name\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.5.1\",\n \"@commitlint/cli\": \"^21.1.0\",\n \"@commitlint/config-conventional\": \"^21.1.0\",\n \"@evilmartians/lefthook\": \"^2.1.9\",\n \"@types/node\": \"24.10.13\",\n \"@types/validate-npm-package-name\": \"^4.0.2\",\n \"@typescript/native-preview\": \"7.0.0-dev.20260628.1\",\n \"rimraf\": \"^6.1.3\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.22.4\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.9\"\n },\n \"packageManager\": \"pnpm@11.8.0\",\n \"commitlint\": {\n \"extends\": [\n \"@commitlint/config-conventional\"\n ],\n \"rules\": {\n \"type-enum\": [\n 2,\n \"always\",\n [\n \"feat\",\n \"fix\",\n \"perf\",\n \"refactor\",\n \"revert\",\n \"docs\",\n \"chore\",\n \"build\",\n \"ci\",\n \"style\",\n \"test\"\n ]\n ]\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { cp, mkdir, rm } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { checkbox } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { addRemote } from '#/add-remote';\nimport { TEMPLATE_URL } from '#/constants';\nimport { type CreateOptions, confirmChoice, showSuccess } from '#/modules/cli';\nimport { cleanTemplate } from '#/utils/clean-template';\nimport { downloadGithubTemplate } from '#/utils/download-template';\nimport { gitAddAll, gitCommit, gitInit } from '#/utils/git';\nimport { listTemplateFiles } from '#/utils/list-template-files';\nimport { createProgress, pauseSpinner, resumeSpinner } from '#/utils/progress';\nimport { optionalModuleFolders, scanOptionalModules } from '#/utils/scan-optional-modules';\nimport { writeScaffoldBase } from '#/utils/write-scaffold-base';\n\n/** Check if a path is a local directory */\nfunction isLocalPath(path: string): boolean {\n return path.startsWith('/') || path.startsWith('./') || path.startsWith('../');\n}\n\nfunction shouldSkipStep(name: 'git' | 'remote'): boolean {\n return process.env[`CREATE_CELLA_SKIP_${name.toUpperCase()}`] === 'true';\n}\n\nexport async function create({\n projectName,\n targetFolder,\n packageManager,\n templateUrl,\n templateRef,\n portOffset,\n adminEmail,\n silent = false,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n // Use custom template or default\n const template = templateUrl || TEMPLATE_URL;\n const isLocalTemplate = templateUrl && isLocalPath(templateUrl);\n\n const progress = createProgress('creating project', silent);\n\n try {\n // Create the target folder\n progress.step('creating project folder');\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n // Upstream commit the scaffold is based on (only known for remote templates). Recorded\n // as sync provenance so the first `pnpm cella sync` can bootstrap a merge-base.\n let baseSha: string | null = null;\n\n // Download or copy the template\n if (isLocalTemplate) {\n progress.step('copying local template');\n const sourcePath = resolve(originalCwd, templateUrl);\n\n // Check if target is inside source (would cause EINVAL). Use a path-boundary check\n // (not a string prefix) so siblings like `cella-try-app` next to `cella` aren't\n // misdetected as being inside the template.\n const rel = relative(sourcePath, targetFolder);\n const targetInsideTemplate = rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));\n if (targetInsideTemplate) {\n throw new Error(\n 'Cannot create project inside the template directory.\\n' +\n ` Run from outside: cd ~ && pnpm create @cellajs/cella ${projectName} --template ${templateUrl}`,\n );\n }\n\n // Copy only what git would track (respects .gitignore: no build output, no local .env secrets).\n const tracked = await listTemplateFiles(sourcePath);\n for (const rel of tracked) {\n const src = join(sourcePath, rel);\n if (!existsSync(src)) continue; // listed but deleted in the working tree\n const dest = join(targetFolder, rel);\n await mkdir(dirname(dest), { recursive: true });\n await cp(src, dest);\n }\n } else {\n // Pin to the chosen ref (release tag or commit SHA), else the default branch.\n const ref = templateRef ?? 'main';\n progress.step(`downloading cella template${templateRef ? ` (${templateRef})` : ''}`);\n baseSha = await downloadGithubTemplate(template, ref, targetFolder);\n }\n\n // Ask which optional modules to keep, then drop deselected paths\n if (!silent) await promptOptionalModules(targetFolder);\n\n // Clean the template, generate configs\n progress.step('cleaning template');\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });\n\n // Record sync provenance before the initial commit so it is tracked. Enables the first\n // `pnpm cella sync` to bootstrap a merge-base against a fork with no shared history.\n if (baseSha) await writeScaffoldBase(targetFolder, baseSha);\n\n // Initialize git repository\n const gitFolderPath = join(targetFolder, '.git');\n if (!shouldSkipStep('git') && !existsSync(gitFolderPath)) {\n progress.step('initializing git');\n await gitInit(targetFolder);\n await gitAddAll(targetFolder);\n await gitCommit(targetFolder, 'Initial commit');\n }\n\n // Add upstream remote\n if (!shouldSkipStep('remote')) {\n progress.step('adding upstream remote');\n await addRemote({ targetFolder, silent: true });\n }\n\n // Done\n progress.done(pc.bold(`created ${projectName}`));\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n // Rethrow so callers (CLI entry, tests) see the real failure. The CLI entry\n // turns this into a non-zero exit; swallowing it here with process.exit hid\n // the underlying error from CI and vitest.\n throw error;\n }\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n // Display final success message\n showSuccess(projectName, targetFolder, relativePath, needsCd, packageManager);\n}\n\n/**\n * Scan the downloaded template for optional modules and let the user deselect them.\n * Deselected modules have their whole folder removed before cleaning. Default keeps\n * everything (parity with previous behavior).\n */\nasync function promptOptionalModules(targetFolder: string): Promise<void> {\n const modules = await scanOptionalModules(targetFolder);\n if (modules.length === 0) return;\n\n // Non-interactive (CI, piped stdin, no TTY): can't render a checkbox — keep every module\n // rather than crashing with \"User force closed the prompt\". Matches the default-keep behavior.\n if (!process.stdin.isTTY) return;\n\n pauseSpinner();\n // Blank line to separate the preceding progress step from this prompt.\n console.info();\n const keep = await checkbox(\n {\n message: 'Optional modules to include',\n // Leading space on the radio icons adds a gap after the cursor (inquirer renders\n // `${cursor}${checkbox} ${name}` with no space between cursor and checkbox).\n theme: { icon: { checked: ` ${pc.green('◉')}`, unchecked: ' ◯' } },\n choices: modules.map((m) => ({\n name: `${m.name} ${pc.dim(`· ${m.description}`)}`,\n value: m.name,\n checked: true,\n })),\n },\n // Clear the answered prompt so we can print a compact, non-bold summary that lists\n // only the kept module names (not their descriptions).\n { clearPromptOnDone: true },\n );\n\n // Keep the choice visible after the prompt clears: plain label plus the selected\n // module names only (not their descriptions), or \"none\" when everything was deselected.\n confirmChoice('Optional modules', keep.length ? keep.join(', ') : pc.dim('none'));\n resumeSpinner();\n\n // Remove every folder owned by a deselected module. `optionalModuleFolders` lists more\n // route variants than any single module uses; `force: true` ignores the ones absent here.\n const folders = modules.filter((m) => !keep.includes(m.name)).flatMap(optionalModuleFolders);\n await Promise.all(folders.map((f) => rm(join(targetFolder, f), { recursive: true, force: true })));\n}\n","import fs from 'node:fs';\nimport git from 'isomorphic-git';\n\n/**\n * Git helpers backed by isomorphic-git (pure JS) instead of shelling out to the\n * `git` binary. This keeps `create-cella` free of `child_process` (no shell access)\n * and removes any dependency on the user's global git config — the initial commit\n * uses an explicit author, so scaffolding never fails with \"Author identity unknown\".\n */\n\n/** Author used for the scaffold's initial commit (users re-author their own commits later). */\nconst INITIAL_AUTHOR = { name: 'cella', email: 'info@cellajs.com' } as const;\n\n/**\n * Initializes a new Git repository in the specified directory.\n */\nexport async function gitInit(repoPath: string): Promise<void> {\n await git.init({ fs, dir: repoPath, defaultBranch: 'main' });\n}\n\n/**\n * Stages all files in the repository (respects `.gitignore`).\n */\nexport async function gitAddAll(repoPath: string): Promise<void> {\n await git.add({ fs, dir: repoPath, filepath: '.' });\n}\n\n/**\n * Creates a commit with the specified message and returns the commit SHA.\n */\nexport async function gitCommit(repoPath: string, message: string): Promise<string> {\n return git.commit({ fs, dir: repoPath, message, author: INITIAL_AUTHOR });\n}\n\n/**\n * Gets the URL of a remote. Throws when the remote does not exist (mirrors\n * `git remote get-url` failing) so callers can detect a missing remote.\n */\nexport async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {\n const remotes = await git.listRemotes({ fs, dir: repoPath });\n const match = remotes.find((r) => r.remote === remoteName);\n if (!match) throw new Error(`No such remote '${remoteName}'`);\n return match.url;\n}\n\n/**\n * Adds a new remote.\n */\nexport async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<void> {\n await git.addRemote({ fs, dir: repoPath, remote: remoteName, url: remoteUrl });\n}\n\n/**\n * Removes a remote.\n */\nexport async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<void> {\n await git.deleteRemote({ fs, dir: repoPath, remote: remoteName });\n}\n","import { CELLA_REMOTE_NAME, CELLA_REMOTE_URL } from '#/constants';\nimport type { AddRemoteOptions } from '#/modules/cli';\nimport { gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove } from '#/utils/git';\n\n/**\n * Adds or updates the upstream remote for the Cella template.\n * @param options - Configuration options\n * @param options.silent - If true, don't throw on failure (used when called from progress tracker)\n */\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = CELLA_REMOTE_NAME,\n silent = false,\n}: AddRemoteOptions): Promise<void> {\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await gitRemoteGetUrl(targetFolder, remoteName);\n } catch {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await gitRemoteRemove(targetFolder, remoteName);\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n }\n } catch (error) {\n if (!silent) {\n throw error;\n }\n }\n}\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { NAME, VERSION } from '#/constants';\nimport { validateProjectName } from '#/utils/validate-project-name';\nimport type { CLIConfig, CLIOptions } from './types';\n\nfunction parsePortOffset(value: string): number {\n const parsed = Number(value);\n\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > 490) {\n throw new InvalidArgumentError('Port offset must be an integer between 0 and 490');\n }\n\n if (parsed % 10 !== 0) {\n throw new InvalidArgumentError('Port offset must be a multiple of 10');\n }\n\n return parsed;\n}\n\n// Initialize CLI variables\nlet directory: string | null = null;\nconst packageManager = 'pnpm';\n\n/**\n * Defines the root CLI command using Commander.\n * This command accepts CLI options and validates user input.\n */\nexport const command = new Command(NAME)\n .version(VERSION, '-v, --version', `output the current version of ${NAME}`)\n .argument('[directory]', 'the directory name for the new project')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'display this help message')\n .option('--template <path>', 'use a custom template (local path or github:user/repo)')\n .option('--port-offset <number>', 'set the port offset (0-490 in steps of 10)', parsePortOffset)\n .option('--admin-email <email>', 'set the admin email for the initial seed user')\n .action((name: string) => {\n const trimmedName = typeof name === 'string' ? name.trim() : name;\n\n if (trimmedName) {\n const validation = validateProjectName(basename(resolve(trimmedName)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(`Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`);\n }\n\n directory = trimmedName;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>();\n\n/**\n * Runs the CLI and returns the parsed configuration.\n * This function parses command line arguments and returns the CLI config.\n */\nexport function runCli(): CLIConfig {\n return {\n options,\n args: command.args,\n directory,\n packageManager,\n };\n}\n\n// Export CLI configuration for direct import\nexport const cli: CLIConfig = runCli();\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],\n };\n}\n","import pc from 'picocolors';\nimport { DESCRIPTION, DIVIDER, getHeaderLine } from '#/constants';\n\n/** ASCII art logo for the CLI welcome screen. */\nfunction showAscii(): void {\n console.info(pc.cyan(' _ _ '));\n console.info(pc.cyan('▒▓█████▓▒ ___ ___| | | __ _ '));\n console.info(pc.cyan('▒▓█ █▓▒ / __/ _ \\\\ | |/ _` | '));\n console.info(pc.cyan('▒▓█ █▓▒ | (_| __/ | | (_| | '));\n console.info(pc.cyan('▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_| '));\n}\n\n/**\n * Displays the compact CLI welcome header.\n */\nexport function showWelcome(): void {\n console.info();\n showAscii();\n console.info();\n console.info(pc.dim(DESCRIPTION));\n console.info();\n console.info(getHeaderLine());\n console.info(DIVIDER);\n}\n\n/**\n * Print a cleared prompt's outcome as a persistent confirmation line:\n * `✓ <label> · <value>`, followed by a blank separator line. Used after prompts\n * that clear themselves on done so the chosen value stays visible.\n */\nexport function confirmChoice(label: string, value: string): void {\n console.info(`${pc.green('✓')} ${label} · ${value}`);\n console.info();\n}\n\n/**\n * Displays the final success message after project creation.\n */\nexport function showSuccess(\n projectName: string,\n _targetFolder: string,\n relativePath: string,\n needsCd: boolean,\n packageManager: string,\n): void {\n console.info();\n\n console.info(pc.bold('Next steps'));\n console.info(DIVIDER);\n console.info();\n\n if (needsCd) {\n console.info(`${pc.dim('$')} cd ${pc.cyan(relativePath)}`);\n }\n console.info(`${pc.dim('$')} ${pc.cyan(`${packageManager} install`)}`);\n console.info(`${pc.dim('$')} ${pc.cyan(`${packageManager} generate`)}`);\n console.info(\n `${pc.dim('$')} ${pc.cyan(`${packageManager} docker`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} seed`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} dev`)}`,\n );\n console.info();\n\n // Point to the generated README for first-run details and sign-in credentials. Use the\n // path relative to the new project so the terminal linkifies the right file (not this CLI's).\n const readmePath = relativePath ? `${relativePath}/README.md` : 'README.md';\n console.info(`${pc.gray('To get started building, please read:')} ${pc.cyan(readmePath)}`);\n console.info();\n console.info(`🥳 enjoy building ${pc.green(projectName)} with cella!`);\n console.info();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport pc from 'picocolors';\nimport {\n generateEnvConfigs,\n generateEnvFromExample,\n getBackendEnvReplacements,\n INITIAL_VERSION,\n PLACEHOLDER_CONFIG,\n TO_CLEAN,\n TO_COPY,\n TO_REMOVE,\n} from '#/constants';\n\nconst warningMark = pc.yellow('⚠');\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder, project name, and optional extra edits.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n displayName,\n portOffset = 0,\n adminEmail = `admin@${projectName}.example.com`,\n}: {\n targetFolder: string;\n projectName: string;\n displayName: string;\n portOffset?: number;\n adminEmail?: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>((resolve, reject) => {\n (async () => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Replace config.default.ts with interpolated placeholder config\n await applyPlaceholderConfig(targetFolder, projectName, displayName);\n\n // Reset release/versioning state so the fork starts under its own identity\n await resetReleaseState(targetFolder, projectName);\n\n // Generate backend .env from backend/.env.example.\n // The backend .env is the single source of truth for the project slug and DB ports\n // (consumed by backend/compose.yaml). There is no root .env.\n const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);\n const backendEnv = await generateEnvFromExample(\n path.resolve(targetFolder, 'backend/.env.example'),\n backendReplacements,\n );\n if (backendEnv) {\n await fs.writeFile(path.resolve(targetFolder, 'backend/.env'), backendEnv, 'utf8');\n }\n\n // Generate minimal env config files with project values and ports baked in\n const envConfigs = generateEnvConfigs(projectName, displayName, portOffset);\n await Promise.all(\n Object.entries(envConfigs).map(([filePath, content]) =>\n fs.writeFile(path.resolve(targetFolder, filePath), content, 'utf8'),\n ),\n );\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n }),\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n }),\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n })();\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nasync function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder. Skip silently if the folder doesn't exist —\n // the template may not include every optional folder (e.g. backend/drizzle).\n let files: string[];\n try {\n files = await fs.readdir(folderPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n\n // `recursive: true` handles nested folders; `force: true` ignores races/missing entries.\n await Promise.all(files.map((file) => fs.rm(path.join(folderPath, file), { recursive: true, force: true })));\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nasync function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nasync function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Read the fork config template shipped inside the cloned Cella template\n * (`shared/config/config.template.ts`), interpolate project tokens, write the\n * result as `shared/config/config.default.ts` — replacing the original — and\n * delete the now-consumed template from the fork.\n */\nasync function applyPlaceholderConfig(targetFolder: string, projectName: string, displayName: string): Promise<void> {\n const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);\n const dest = path.resolve(targetFolder, './shared/config/config.default.ts');\n\n try {\n let content = await fs.readFile(src, 'utf8');\n content = content.replaceAll('__project_name__', displayName);\n content = content.replaceAll('__project_slug__', projectName);\n await fs.writeFile(dest, content, 'utf8');\n await fs.rm(src, { force: true });\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Fork config template \"${src}\" not found > Skip`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Reset the upstream cella release identity so the new fork starts clean:\n * - root `package.json`: `name` → project slug, `version` → INITIAL_VERSION,\n * and blank out `license`/`repository`/`description`/`keywords`/`author`/`homepage`\n * (keys kept visible so the fork can fill in its own metadata)\n * - `.github/release-please-manifest.json` → `{ \".\": INITIAL_VERSION }`\n * - `.github/release-please-config.json`: `changelog-path` → root `CHANGELOG.md`\n * (cella tracks its own changelog in the template docs folder, which the fork\n * keeps as upstream reference; the fork's own releases write to a fresh root changelog)\n * - `CHANGELOG.md` → fresh stub\n */\nasync function resetReleaseState(targetFolder: string, projectName: string): Promise<void> {\n // Root package.json: rewrite only the top-level fields in place to avoid reformatting\n // the whole file. Set name/version to the fork's identity, then blank out the upstream\n // cella metadata (license, repository, description, keywords, author, homepage) while\n // keeping the keys visible so the fork can fill them in.\n const pkgPath = path.resolve(targetFolder, 'package.json');\n try {\n let pkg = await fs.readFile(pkgPath, 'utf8');\n pkg = pkg.replace(/^(\\s*\"name\":\\s*)\"[^\"]*\"/m, `$1\"${projectName}\"`);\n pkg = pkg.replace(/^(\\s*\"version\":\\s*)\"[^\"]*\"/m, `$1\"${INITIAL_VERSION}\"`);\n // Empty each string field, keeping the key so the fork sees the shape.\n for (const field of ['license', 'repository', 'description', 'author', 'homepage']) {\n pkg = pkg.replace(new RegExp(`^(\\\\s*\"${field}\":\\\\s*)\"[^\"]*\"`, 'm'), '$1\"\"');\n }\n // Collapse the multi-line keywords array to an empty one.\n pkg = pkg.replace(/^(\\s*\"keywords\":\\s*)\\[[^\\]]*\\]/m, '$1[]');\n await fs.writeFile(pkgPath, pkg, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} \"package.json\" not found > Skip version reset`);\n } else {\n throw err;\n }\n }\n\n // Point release-please at a root CHANGELOG.md for the fork. The upstream Cella\n // changelog stays wherever the template keeps it; the fork's own releases live at root.\n const configPath = path.resolve(targetFolder, '.github/release-please-config.json');\n try {\n const config = JSON.parse(await fs.readFile(configPath, 'utf8')) as {\n packages?: Record<string, Record<string, unknown>>;\n };\n config.packages ??= {};\n config.packages['.'] ??= {};\n config.packages['.']['changelog-path'] = 'CHANGELOG.md';\n await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} \"release-please-config.json\" not found > Skip changelog path reset`);\n } else {\n throw err;\n }\n }\n\n // Reset the release-please manifest and changelog. `force: true` writes even if absent.\n await fs.writeFile(\n path.resolve(targetFolder, '.github/release-please-manifest.json'),\n `{\\n \".\": \"${INITIAL_VERSION}\"\\n}\\n`,\n 'utf8',\n );\n await fs.writeFile(path.resolve(targetFolder, 'CHANGELOG.md'), '# Changelog\\n', 'utf8');\n}\n","import fs from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport git from 'isomorphic-git';\nimport http from 'isomorphic-git/http/node';\n\n/** Parse `github:owner/repo` or `owner/repo` into its owner and repo parts. */\nfunction parseGithubRepo(template: string): { owner: string; repo: string } {\n const [owner, repo] = template.replace(/^github:/, '').split('/');\n if (!owner || !repo) {\n throw new Error(`Invalid template '${template}'. Expected 'github:owner/repo'.`);\n }\n return { owner, repo };\n}\n\n/**\n * Clones a GitHub template into `targetFolder` using isomorphic-git (pure JS).\n *\n * A shallow, single-branch clone over HTTPS — no `git` binary, no `child_process`,\n * so `create-cella` keeps a clean supply-chain profile (no shell access). Unlike a\n * raw tarball parse, isomorphic-git reconstructs the tree itself, so file modes and\n * long paths are always correct. The cloned `.git` history is discarded afterwards;\n * scaffolding re-initialises the repo with a fresh initial commit.\n *\n * @param template - `github:owner/repo` (or `owner/repo`).\n * @param ref - Branch or tag to check out (e.g. `main` or a release tag).\n * @param targetFolder - Directory to clone the template into.\n * @returns The exact upstream commit SHA the template was scaffolded from.\n */\nexport async function downloadGithubTemplate(template: string, ref: string, targetFolder: string): Promise<string> {\n const { owner, repo } = parseGithubRepo(template);\n const url = `https://github.com/${owner}/${repo}.git`;\n\n await git.clone({ fs, http, dir: targetFolder, url, ref, singleBranch: true, depth: 1 });\n\n // Capture the exact commit the scaffold is based on before discarding history. This is\n // recorded as sync provenance so `pnpm cella sync` can bootstrap a merge-base — the fork\n // otherwise shares no history with upstream and the first sync would have nothing to diff.\n const baseSha = await git.resolveRef({ fs, dir: targetFolder, ref: 'HEAD' });\n\n // Drop the cloned history — the scaffold gets its own fresh initial commit.\n await rm(join(targetFolder, '.git'), { recursive: true, force: true });\n\n return baseSha;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ignore from 'ignore';\n\n/**\n * Lists the files under `root` that git would track, without shelling out to git.\n *\n * Honors the root `.gitignore` (via the `ignore` package) and always skips `.git`\n * and `node_modules`. Used for the local-template dev flow (`--template ./local`)\n * so build output and local `.env` secrets aren't copied into the new project.\n *\n * Paths are returned relative to `root` using POSIX separators.\n */\nexport async function listTemplateFiles(root: string): Promise<string[]> {\n const ig = ignore().add(['.git', 'node_modules']);\n try {\n ig.add(await readFile(join(root, '.gitignore'), 'utf8'));\n } catch {\n // No .gitignore at the template root — only the defaults above apply.\n }\n\n const files: string[] = [];\n\n async function walk(relDir: string): Promise<void> {\n const entries = await readdir(relDir ? join(root, relDir) : root, { withFileTypes: true });\n for (const entry of entries) {\n const rel = relDir ? `${relDir}/${entry.name}` : entry.name;\n // `ignore` matches directory rules when the path ends with a slash.\n if (ig.ignores(entry.isDirectory() ? `${rel}/` : rel)) continue;\n if (entry.isDirectory()) await walk(rel);\n else if (entry.isFile()) files.push(rel);\n }\n }\n\n await walk('');\n return files;\n}\n","/**\n * Progress Tracker Utility\n *\n * Provides compact progress tracking for CLI operations.\n * Uses a single spinner that updates in place.\n */\n\nimport ora, { type Ora } from 'ora';\nimport pc from 'picocolors';\n\n/** Global reference to the currently active spinner */\nlet activeSpinner: Ora | null = null;\n\n/** Pause the active spinner (for interactive prompts) */\nexport function pauseSpinner(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n}\n\n/** Resume the active spinner after a prompt */\nexport function resumeSpinner(): void {\n if (activeSpinner) {\n activeSpinner.start();\n }\n}\n\nexport interface ProgressTracker {\n /** Update the current step (shown as spinner text) */\n step: (message: string) => void;\n /** Mark the tracker as complete with final message */\n done: (message: string) => void;\n /** Mark the tracker as failed with error message */\n fail: (message: string) => void;\n}\n\n/**\n * Creates a progress tracker for a multi-step operation.\n * Shows a single spinner that updates in place.\n *\n * @param title - The initial title for the spinner\n * @returns A ProgressTracker instance\n *\n * @example\n * const progress = createProgress('creating project');\n * progress.step('downloading template');\n * progress.step('installing dependencies');\n * progress.done('project created');\n */\nexport function createProgress(title: string, silent = false): ProgressTracker {\n const completedSteps: string[] = [];\n\n const spinner = ora({\n text: title,\n isSilent: silent,\n });\n\n activeSpinner = spinner;\n spinner.start();\n\n // Helper to log (respects silent mode)\n const log = (msg: string) => {\n if (!silent) console.info(msg);\n };\n\n return {\n step: (message: string) => {\n // Complete previous step with green check if there was one\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n }\n completedSteps.push(message);\n spinner.text = message;\n spinner.start();\n },\n\n done: (message: string) => {\n // Complete the last step with green check\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n } else {\n spinner.stop();\n }\n activeSpinner = null;\n if (message) {\n log('');\n log(`${pc.green('✓')} ${message}`);\n }\n },\n\n fail: (message: string) => {\n spinner.stop();\n activeSpinner = null;\n log(`${pc.red('✗')} ${pc.red(message)}`);\n },\n };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\n\n/** An optional module discovered in a `*-module.ts` registration. */\nexport interface OptionalModule {\n name: string;\n description: string;\n /** Module folder, relative to the project root, removed when deselected. */\n folder: string;\n}\n\n/** Directories never worth scanning. */\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.turbo', 'coverage']);\n\n/**\n * Scan the template for `*-module.ts` files that register `optional: true` and return\n * their name, description and folder. Pure static regex parsing — no imports/eval — so it\n * runs before dependencies are installed. The folder is the module file's directory.\n */\nexport async function scanOptionalModules(targetFolder: string): Promise<OptionalModule[]> {\n const files: string[] = [];\n await collectModuleFiles(targetFolder, files);\n\n const modules: OptionalModule[] = [];\n for (const file of files) {\n const src = await readFile(file, 'utf8');\n if (!/optional:\\s*true/.test(src)) continue;\n\n const name = src.match(/name:\\s*['\"]([^'\"]+)['\"]/)?.[1];\n const description = src.match(/description:\\s*['\"]([^'\"]+)['\"]/)?.[1] ?? '';\n if (!name) continue;\n\n modules.push({ name, description, folder: relative(targetFolder, dirname(file)) });\n }\n return modules.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Every project-root-relative folder removed when an optional module is deselected: its own\n * module folder plus the route and static-asset folders it may own. Route folders are\n * speculative — a module owns either a pathless `_<name>` (URL-transparent) or a path-based\n * `<name>` route, under `routes/_public` and/or `routes/_app` — so callers remove only the\n * variants that actually exist. Single-sourced here so removal and tests stay in sync.\n */\nexport function optionalModuleFolders({ name, folder }: OptionalModule): string[] {\n return [\n folder,\n `frontend/src/routes/_public/_${name}`,\n `frontend/src/routes/_public/${name}`,\n `frontend/src/routes/_app/_${name}`,\n `frontend/src/routes/_app/${name}`,\n `frontend/public/static/${name}`,\n ];\n}\n\nasync function collectModuleFiles(dir: string, out: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await collectModuleFiles(join(dir, entry.name), out);\n } else if (entry.name.endsWith('-module.ts')) {\n out.push(join(dir, entry.name));\n }\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/** Directory (relative to the project root) holding cella sync metadata. */\nconst CELLA_DIR = '.cella';\n\n/** Committed file recording the upstream commit a scaffold was based on. */\nconst BASE_FILE = 'base';\n\n/**\n * Record the upstream cella commit a scaffold was created from.\n *\n * A create-cella project starts from a template snapshot with a fresh, rootless initial\n * commit — it shares no git history with upstream cella. Without a recorded base, the first\n * `pnpm cella sync` has no merge-base and fails (\"no common ancestor\"). Writing the exact\n * source commit to `.cella/base` (committed) lets the sync CLI bootstrap a merge-base by\n * grafting the fork's root commit onto this base after fetching upstream.\n *\n * The file is human-readable (leading comments) but the sync CLI only reads the 40-char SHA,\n * so the comments are safe to keep.\n *\n * @param targetFolder - The project root.\n * @param sha - The upstream commit SHA the scaffold was based on.\n */\nexport async function writeScaffoldBase(targetFolder: string, sha: string): Promise<void> {\n const dir = join(targetFolder, CELLA_DIR);\n await mkdir(dir, { recursive: true });\n const contents =\n '# Upstream cella commit this project was scaffolded from (create-cella).\\n' +\n '# Used by `pnpm cella sync` to bootstrap the first merge-base. Do not edit or delete.\\n' +\n `${sha}\\n`;\n await writeFile(join(dir, BASE_FILE), contents, 'utf8');\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\ninterface UsedPorts {\n project: string;\n frontend: number;\n backend: number;\n offset: number;\n}\n\n/**\n * Scan sibling directories for existing cella forks and extract their dev ports.\n * Looks for `shared/config/config.development.ts` to identify cella-based projects.\n */\nexport async function detectUsedPorts(targetFolder: string): Promise<UsedPorts[]> {\n const parentDir = dirname(targetFolder);\n const used: UsedPorts[] = [];\n\n let siblings: string[];\n try {\n siblings = await readdir(parentDir);\n } catch {\n return used;\n }\n\n for (const name of siblings) {\n const configPath = join(parentDir, name, 'shared/config/config.development.ts');\n try {\n const content = await readFile(configPath, 'utf8');\n const feMatch = content.match(/frontendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n const beMatch = content.match(/backendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n if (feMatch && beMatch) {\n const frontend = Number(feMatch[1]);\n const backend = Number(beMatch[1]);\n used.push({\n project: name,\n frontend,\n backend,\n offset: frontend - 3000,\n });\n }\n } catch {\n // Not a cella fork, skip\n }\n }\n\n return used;\n}\n\n/** Find the next available offset (in steps of 10) that doesn't collide with existing forks. */\nexport function findNextOffset(usedPorts: UsedPorts[]): number {\n const usedOffsets = new Set(usedPorts.map((p) => p.offset));\n for (let offset = 0; offset <= 490; offset += 10) {\n if (!usedOffsets.has(offset)) return offset;\n }\n return 0;\n}\n","/**\n * Fetch lightweight metadata about the cella template from the GitHub API so the\n * scaffolder can offer \"latest release\" vs \"latest commit\" as a starting point.\n *\n * Both helpers fail soft (return null) on network/API errors or rate limits — the\n * caller falls back to the default branch when metadata is unavailable.\n */\nimport { TEMPLATE_REPOSITORY } from '#/constants';\n\nconst TEMPLATE_METADATA_API_BASE = `https://api.github.com/repos/${TEMPLATE_REPOSITORY}`;\n\n/** Format an ISO timestamp as a locale-independent YYYY-MM-DD date. */\nfunction toDate(iso?: string): string {\n return iso ? iso.slice(0, 10) : 'unknown';\n}\n\nconst GITHUB_HEADERS = { Accept: 'application/vnd.github+json' } as const;\n\nexport interface ReleaseInfo {\n /** Release tag, e.g. 'v0.5.0' */\n tag: string;\n /** Publication date, YYYY-MM-DD */\n date: string;\n}\n\nexport interface CommitInfo {\n /** Full commit SHA (used for a reproducible checkout) */\n sha: string;\n /** Short 7-char SHA for display */\n shortSha: string;\n /** First line of the commit message */\n message: string;\n /** Commit date, YYYY-MM-DD */\n date: string;\n}\n\n/**\n * Fetch the latest published template release (`v*` tag).\n *\n * cella publishes two release series — the template (`v*`) and the scaffolder\n * (`create-cella-v*`). We list releases (newest first) and pick the first\n * non-draft, non-prerelease `v*` release so the scaffolder version is ignored.\n */\nexport async function fetchLatestRelease(): Promise<ReleaseInfo | null> {\n const apiUrl = `${TEMPLATE_METADATA_API_BASE}/releases?per_page=30`;\n\n try {\n const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });\n if (!response.ok) return null;\n\n const releases = (await response.json()) as Array<{\n tag_name?: string;\n published_at?: string;\n draft?: boolean;\n prerelease?: boolean;\n }>;\n\n const release = releases.find((r) => !r.draft && !r.prerelease && /^v?\\d/.test(r.tag_name ?? ''));\n if (!release?.tag_name) return null;\n\n return { tag: release.tag_name, date: toDate(release.published_at) };\n } catch {\n return null;\n }\n}\n\n/** Fetch the latest commit on a branch (defaults to 'main'). */\nexport async function fetchLatestCommit(branch = 'main'): Promise<CommitInfo | null> {\n const apiUrl = `${TEMPLATE_METADATA_API_BASE}/commits/${branch}`;\n\n try {\n const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n sha?: string;\n commit?: { message?: string; author?: { date?: string } };\n };\n if (!data.sha) return null;\n\n const message = (data.commit?.message ?? '').split('\\n')[0];\n return { sha: data.sha, shortSha: data.sha.slice(0, 7), message, date: toDate(data.commit?.author?.date) };\n } catch {\n return null;\n }\n}\n","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n *\n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n"],"mappings":"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","names":["existsSync","rm","basename","resolve","pc","readFile","mkdir","rm","dirname","join","relative","resolve","pc","pc","pc","packageManager","fs","pc","pc","resolve","fs","fs","git","join","pc","readdir","readFile","join","join","path","packageManager","mkdir","resolve","relative","rel","join","dirname","pc","rm","readdir","readFile","dirname","join","readdir","path","basename","resolve","existsSync","rm","pc"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cellajs/create-cella",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Cella is a TypeScript template to create web apps with sync and offline capabilities.",