@cellajs/create-cella 0.3.1 → 0.3.3
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 +3 -16
- package/dist/index.js +119 -95
- package/dist/index.js.map +1 -1
- package/package.json +1 -3
- package/configs/default-config.ts.template +0 -402
package/README.md
CHANGED
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
# @cellajs/create-cella
|
|
2
2
|
|
|
3
|
-
CLI tool to scaffold a new Cella project from the template.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This CLI creates a new Cella project by downloading the latest template, scaffolding your project, and configuring git with upstream tracking for future syncs. It runs no shell commands and requires no `git` binary — git operations use [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) and the template is fetched and extracted in-process.
|
|
3
|
+
CLI tool to scaffold a new Cella project from [the template](https://github.com/cellajs/cella).
|
|
8
4
|
|
|
9
5
|
## Usage
|
|
10
6
|
|
|
11
7
|
```bash
|
|
12
8
|
pnpm create @cellajs/cella my-app
|
|
13
9
|
```
|
|
14
|
-
|
|
15
|
-
Running without arguments starts interactive mode, prompting for:
|
|
16
|
-
|
|
17
|
-
1. **Project name** – Directory name and package name
|
|
18
|
-
2. **New branch** – Optionally create a dev branch alongside `main`
|
|
19
|
-
3. **Directory conflict** – If target exists, choose to cancel or continue
|
|
20
|
-
|
|
21
10
|
## CLI Options
|
|
22
11
|
|
|
23
12
|
```bash
|
|
@@ -34,14 +23,12 @@ pnpm create @cellajs/cella [directory] [options]
|
|
|
34
23
|
|
|
35
24
|
## What It Does
|
|
36
25
|
|
|
37
|
-
1. Downloads the latest Cella template as a GitHub tarball and extracts it in-process
|
|
38
|
-
2. Cleans template files
|
|
26
|
+
1. Downloads the latest Cella template as a GitHub tarball and extracts it in-process
|
|
27
|
+
2. Cleans template files to replace cella-specific config
|
|
39
28
|
3. Initializes a git repository with an initial commit via isomorphic-git
|
|
40
29
|
4. Creates an optional working branch
|
|
41
30
|
5. Adds Cella as the upstream remote for future syncs
|
|
42
31
|
|
|
43
|
-
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).
|
|
44
|
-
|
|
45
32
|
## Development
|
|
46
33
|
|
|
47
34
|
```bash
|
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.
|
|
16
|
+
version: "0.3.3",
|
|
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.",
|
|
@@ -31,7 +32,6 @@ var package_default = {
|
|
|
31
32
|
type: "module",
|
|
32
33
|
main: "./dist/index.js",
|
|
33
34
|
files: [
|
|
34
|
-
"configs",
|
|
35
35
|
"dist",
|
|
36
36
|
"index.js",
|
|
37
37
|
"README.md"
|
|
@@ -63,7 +63,6 @@ var package_default = {
|
|
|
63
63
|
commander: "^15.0.0",
|
|
64
64
|
ignore: "^7.0.5",
|
|
65
65
|
"isomorphic-git": "^1.38.5",
|
|
66
|
-
nanotar: "^0.3.0",
|
|
67
66
|
ora: "^9.4.1",
|
|
68
67
|
picocolors: "^1.1.1",
|
|
69
68
|
"validate-npm-package-name": "^8.0.0"
|
|
@@ -114,23 +113,25 @@ var NAME = "create cella";
|
|
|
114
113
|
var DIVIDER = "\u2500".repeat(60);
|
|
115
114
|
var TEMPLATE_URL = "github:cellajs/cella";
|
|
116
115
|
var CELLA_REMOTE_URL = "git@github.com:cellajs/cella.git";
|
|
116
|
+
var CELLA_REMOTE_NAME = "cella-upstream";
|
|
117
117
|
var DESCRIPTION = package_default.description;
|
|
118
118
|
var VERSION = package_default.version;
|
|
119
119
|
var WEBSITE = package_default.homepage;
|
|
120
|
-
function getHeaderLine(
|
|
121
|
-
const leftText = `\u29C8 ${NAME} \xB7 ${VERSION}
|
|
122
|
-
const left = `${pc.cyan(`\u29C8 ${NAME}`)} ${pc.dim(`\xB7 ${VERSION}
|
|
120
|
+
function getHeaderLine() {
|
|
121
|
+
const leftText = `\u29C8 ${NAME} \xB7 ${VERSION}`;
|
|
122
|
+
const left = `${pc.cyan(`\u29C8 ${NAME}`)} ${pc.dim(`\xB7 ${VERSION}`)}`;
|
|
123
123
|
const right = pc.cyan(WEBSITE);
|
|
124
124
|
const padding = Math.max(1, 60 - leftText.length - WEBSITE.length);
|
|
125
125
|
return `${left}${" ".repeat(padding)}${right}`;
|
|
126
126
|
}
|
|
127
|
-
var TO_REMOVE = ["./cli/create", "./info/QUICKSTART.md"];
|
|
127
|
+
var TO_REMOVE = ["./cli/create", "./info/QUICKSTART.md", "./infra/Pulumi.production.yaml"];
|
|
128
128
|
var TO_CLEAN = ["./backend/drizzle"];
|
|
129
129
|
var TO_COPY = {
|
|
130
130
|
"./frontend/.env.example": "./frontend/.env",
|
|
131
131
|
"./info/QUICKSTART.md": "README.md"
|
|
132
132
|
};
|
|
133
|
-
var PLACEHOLDER_CONFIG = "
|
|
133
|
+
var PLACEHOLDER_CONFIG = "shared/config/config.template.ts";
|
|
134
|
+
var INITIAL_VERSION = "0.0.0";
|
|
134
135
|
async function generateEnvFromExample(examplePath, replacements) {
|
|
135
136
|
const { readFile: readFile4 } = await import("fs/promises");
|
|
136
137
|
let content;
|
|
@@ -220,8 +221,8 @@ ${nameEntry}${body}
|
|
|
220
221
|
|
|
221
222
|
// src/create.ts
|
|
222
223
|
import { existsSync } from "fs";
|
|
223
|
-
import { cp, mkdir as mkdir2, rm } from "fs/promises";
|
|
224
|
-
import { dirname as
|
|
224
|
+
import { cp, mkdir as mkdir2, rm as rm2 } from "fs/promises";
|
|
225
|
+
import { dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve as resolve2 } from "path";
|
|
225
226
|
import { checkbox } from "@inquirer/prompts";
|
|
226
227
|
import pc5 from "picocolors";
|
|
227
228
|
|
|
@@ -255,7 +256,7 @@ async function gitRemoteRemove(repoPath, remoteName) {
|
|
|
255
256
|
async function addRemote({
|
|
256
257
|
targetFolder,
|
|
257
258
|
remoteUrl = CELLA_REMOTE_URL,
|
|
258
|
-
remoteName =
|
|
259
|
+
remoteName = CELLA_REMOTE_NAME,
|
|
259
260
|
silent = false
|
|
260
261
|
}) {
|
|
261
262
|
try {
|
|
@@ -338,13 +339,13 @@ function showAscii() {
|
|
|
338
339
|
console.info(pc2.cyan("\u2592\u2593\u2588 \u2588\u2593\u2592 | (_| __/ | | (_| | "));
|
|
339
340
|
console.info(pc2.cyan("\u2592\u2593\u2588\u2588\u2588\u2588\u2588\u2593\u2592 \\___\\___|_|_|\\__,_| "));
|
|
340
341
|
}
|
|
341
|
-
function showWelcome(
|
|
342
|
+
function showWelcome() {
|
|
342
343
|
console.info();
|
|
343
344
|
showAscii();
|
|
344
345
|
console.info();
|
|
345
346
|
console.info(pc2.dim(DESCRIPTION));
|
|
346
347
|
console.info();
|
|
347
|
-
console.info(getHeaderLine(
|
|
348
|
+
console.info(getHeaderLine());
|
|
348
349
|
console.info(DIVIDER);
|
|
349
350
|
}
|
|
350
351
|
function confirmChoice(label, value) {
|
|
@@ -375,21 +376,8 @@ function showSuccess(projectName, _targetFolder, relativePath, needsCd, packageM
|
|
|
375
376
|
// src/utils/clean-template.ts
|
|
376
377
|
import fs2 from "fs/promises";
|
|
377
378
|
import path from "path";
|
|
378
|
-
import { fileURLToPath } from "url";
|
|
379
379
|
import pc3 from "picocolors";
|
|
380
380
|
var warningMark = pc3.yellow("\u26A0");
|
|
381
|
-
async function resolvePlaceholderConfigPath() {
|
|
382
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
383
|
-
const candidates = [path.resolve(here, "../..", PLACEHOLDER_CONFIG), path.resolve(here, "..", PLACEHOLDER_CONFIG)];
|
|
384
|
-
for (const candidate of candidates) {
|
|
385
|
-
try {
|
|
386
|
-
await fs2.access(candidate);
|
|
387
|
-
return candidate;
|
|
388
|
-
} catch {
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return candidates[0];
|
|
392
|
-
}
|
|
393
381
|
async function cleanTemplate({
|
|
394
382
|
targetFolder,
|
|
395
383
|
projectName,
|
|
@@ -408,7 +396,8 @@ async function cleanTemplate({
|
|
|
408
396
|
const destAbsolutePath = path.resolve(targetFolder, dest);
|
|
409
397
|
await copyFile(srcAbsolutePath, destAbsolutePath);
|
|
410
398
|
}
|
|
411
|
-
await applyPlaceholderConfig(targetFolder, projectName);
|
|
399
|
+
await applyPlaceholderConfig(targetFolder, projectName, displayName);
|
|
400
|
+
await resetReleaseState(targetFolder, projectName);
|
|
412
401
|
const backendReplacements = getBackendEnvReplacements(projectName, adminEmail, portOffset);
|
|
413
402
|
const backendEnv = await generateEnvFromExample(
|
|
414
403
|
path.resolve(targetFolder, "backend/.env.example"),
|
|
@@ -443,18 +432,14 @@ async function cleanTemplate({
|
|
|
443
432
|
});
|
|
444
433
|
}
|
|
445
434
|
async function removeFolderContents(folderPath) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
files.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
await fs2.rm(filePath);
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
);
|
|
435
|
+
let files;
|
|
436
|
+
try {
|
|
437
|
+
files = await fs2.readdir(folderPath);
|
|
438
|
+
} catch (err) {
|
|
439
|
+
if (err.code === "ENOENT") return;
|
|
440
|
+
throw err;
|
|
441
|
+
}
|
|
442
|
+
await Promise.all(files.map((file) => fs2.rm(path.join(folderPath, file), { recursive: true, force: true })));
|
|
458
443
|
}
|
|
459
444
|
async function removeFileOrFolder(pathToRemove) {
|
|
460
445
|
await fs2.rm(pathToRemove, { recursive: true, force: true });
|
|
@@ -473,29 +458,72 @@ ${warningMark} Source file "${src}" does not exist > Skip copy`);
|
|
|
473
458
|
}
|
|
474
459
|
}
|
|
475
460
|
}
|
|
476
|
-
async function applyPlaceholderConfig(targetFolder, projectName) {
|
|
477
|
-
const
|
|
478
|
-
const src = await resolvePlaceholderConfigPath();
|
|
461
|
+
async function applyPlaceholderConfig(targetFolder, projectName, displayName) {
|
|
462
|
+
const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);
|
|
479
463
|
const dest = path.resolve(targetFolder, "./shared/config/config.default.ts");
|
|
480
464
|
try {
|
|
481
465
|
let content = await fs2.readFile(src, "utf8");
|
|
482
466
|
content = content.replaceAll("__project_name__", displayName);
|
|
483
467
|
content = content.replaceAll("__project_slug__", projectName);
|
|
484
468
|
await fs2.writeFile(dest, content, "utf8");
|
|
469
|
+
await fs2.rm(src, { force: true });
|
|
485
470
|
} catch (err) {
|
|
486
471
|
if (err.code === "ENOENT") {
|
|
487
472
|
console.info(`
|
|
488
|
-
${warningMark}
|
|
473
|
+
${warningMark} Fork config template "${src}" not found > Skip`);
|
|
489
474
|
} else {
|
|
490
475
|
throw err;
|
|
491
476
|
}
|
|
492
477
|
}
|
|
493
478
|
}
|
|
479
|
+
async function resetReleaseState(targetFolder, projectName) {
|
|
480
|
+
const pkgPath = path.resolve(targetFolder, "package.json");
|
|
481
|
+
try {
|
|
482
|
+
let pkg = await fs2.readFile(pkgPath, "utf8");
|
|
483
|
+
pkg = pkg.replace(/^(\s*"name":\s*)"[^"]*"/m, `$1"${projectName}"`);
|
|
484
|
+
pkg = pkg.replace(/^(\s*"version":\s*)"[^"]*"/m, `$1"${INITIAL_VERSION}"`);
|
|
485
|
+
for (const field of ["license", "repository", "description", "author", "homepage"]) {
|
|
486
|
+
pkg = pkg.replace(new RegExp(`^(\\s*"${field}":\\s*)"[^"]*"`, "m"), '$1""');
|
|
487
|
+
}
|
|
488
|
+
pkg = pkg.replace(/^(\s*"keywords":\s*)\[[^\]]*\]/m, "$1[]");
|
|
489
|
+
await fs2.writeFile(pkgPath, pkg, "utf8");
|
|
490
|
+
} catch (err) {
|
|
491
|
+
if (err.code === "ENOENT") {
|
|
492
|
+
console.info(`
|
|
493
|
+
${warningMark} "package.json" not found > Skip version reset`);
|
|
494
|
+
} else {
|
|
495
|
+
throw err;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const configPath = path.resolve(targetFolder, ".github/release-please-config.json");
|
|
499
|
+
try {
|
|
500
|
+
const config = await fs2.readFile(configPath, "utf8");
|
|
501
|
+
await fs2.writeFile(configPath, config.replaceAll("info/CHANGELOG.md", "CHANGELOG.md"), "utf8");
|
|
502
|
+
} catch (err) {
|
|
503
|
+
if (err.code === "ENOENT") {
|
|
504
|
+
console.info(`
|
|
505
|
+
${warningMark} "release-please-config.json" not found > Skip changelog path reset`);
|
|
506
|
+
} else {
|
|
507
|
+
throw err;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
await fs2.writeFile(
|
|
511
|
+
path.resolve(targetFolder, ".github/release-please-manifest.json"),
|
|
512
|
+
`{
|
|
513
|
+
".": "${INITIAL_VERSION}"
|
|
514
|
+
}
|
|
515
|
+
`,
|
|
516
|
+
"utf8"
|
|
517
|
+
);
|
|
518
|
+
await fs2.writeFile(path.resolve(targetFolder, "CHANGELOG.md"), "# Changelog\n", "utf8");
|
|
519
|
+
}
|
|
494
520
|
|
|
495
521
|
// src/utils/download-template.ts
|
|
496
|
-
import
|
|
497
|
-
import {
|
|
498
|
-
import {
|
|
522
|
+
import fs3 from "fs";
|
|
523
|
+
import { rm } from "fs/promises";
|
|
524
|
+
import { join } from "path";
|
|
525
|
+
import git2 from "isomorphic-git";
|
|
526
|
+
import http from "isomorphic-git/http/node";
|
|
499
527
|
function parseGithubRepo(template) {
|
|
500
528
|
const [owner, repo] = template.replace(/^github:/, "").split("/");
|
|
501
529
|
if (!owner || !repo) {
|
|
@@ -505,22 +533,11 @@ function parseGithubRepo(template) {
|
|
|
505
533
|
}
|
|
506
534
|
async function downloadGithubTemplate(template, ref, targetFolder) {
|
|
507
535
|
const { owner, repo } = parseGithubRepo(template);
|
|
508
|
-
const url = `https://
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const archive = new Uint8Array(await response.arrayBuffer());
|
|
514
|
-
const entries = await parseTarGzip(archive);
|
|
515
|
-
for (const entry of entries) {
|
|
516
|
-
if (entry.type !== "file" || !entry.data) continue;
|
|
517
|
-
const relativePath = entry.name.replace(/^[^/]+\//, "");
|
|
518
|
-
if (!relativePath) continue;
|
|
519
|
-
const dest = join(targetFolder, relativePath);
|
|
520
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
521
|
-
const mode = entry.attrs?.mode ? Number.parseInt(entry.attrs.mode, 8) : void 0;
|
|
522
|
-
await writeFile(dest, entry.data, mode ? { mode } : void 0);
|
|
523
|
-
}
|
|
536
|
+
const url = `https://github.com/${owner}/${repo}.git`;
|
|
537
|
+
await git2.clone({ fs: fs3, http, dir: targetFolder, url, ref, singleBranch: true, depth: 1 });
|
|
538
|
+
const baseSha = await git2.resolveRef({ fs: fs3, dir: targetFolder, ref: "HEAD" });
|
|
539
|
+
await rm(join(targetFolder, ".git"), { recursive: true, force: true });
|
|
540
|
+
return baseSha;
|
|
524
541
|
}
|
|
525
542
|
|
|
526
543
|
// src/utils/list-template-files.ts
|
|
@@ -605,7 +622,7 @@ function createProgress(title, silent = false) {
|
|
|
605
622
|
|
|
606
623
|
// src/utils/scan-optional-modules.ts
|
|
607
624
|
import { readdir as readdir2, readFile as readFile2 } from "fs/promises";
|
|
608
|
-
import { dirname
|
|
625
|
+
import { dirname, join as join3, relative } from "path";
|
|
609
626
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".turbo", "coverage"]);
|
|
610
627
|
async function scanOptionalModules(targetFolder) {
|
|
611
628
|
const files = [];
|
|
@@ -617,7 +634,7 @@ async function scanOptionalModules(targetFolder) {
|
|
|
617
634
|
const name = src.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
|
|
618
635
|
const description = src.match(/description:\s*['"]([^'"]+)['"]/)?.[1] ?? "";
|
|
619
636
|
if (!name) continue;
|
|
620
|
-
modules.push({ name, description, folder: relative(targetFolder,
|
|
637
|
+
modules.push({ name, description, folder: relative(targetFolder, dirname(file)) });
|
|
621
638
|
}
|
|
622
639
|
return modules.sort((a, b) => a.name.localeCompare(b.name));
|
|
623
640
|
}
|
|
@@ -643,6 +660,21 @@ async function collectModuleFiles(dir, out) {
|
|
|
643
660
|
}
|
|
644
661
|
}
|
|
645
662
|
|
|
663
|
+
// src/utils/write-scaffold-base.ts
|
|
664
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
665
|
+
import { join as join4 } from "path";
|
|
666
|
+
var CELLA_DIR = ".cella";
|
|
667
|
+
var BASE_FILE = "base";
|
|
668
|
+
async function writeScaffoldBase(targetFolder, sha) {
|
|
669
|
+
const dir = join4(targetFolder, CELLA_DIR);
|
|
670
|
+
await mkdir(dir, { recursive: true });
|
|
671
|
+
const contents = `# Upstream cella commit this project was scaffolded from (create-cella).
|
|
672
|
+
# Used by \`pnpm cella sync\` to bootstrap the first merge-base. Do not edit or delete.
|
|
673
|
+
${sha}
|
|
674
|
+
`;
|
|
675
|
+
await writeFile(join4(dir, BASE_FILE), contents, "utf8");
|
|
676
|
+
}
|
|
677
|
+
|
|
646
678
|
// src/create.ts
|
|
647
679
|
function isLocalPath(path2) {
|
|
648
680
|
return path2.startsWith("/") || path2.startsWith("./") || path2.startsWith("../");
|
|
@@ -668,6 +700,7 @@ async function create({
|
|
|
668
700
|
progress.step("creating project folder");
|
|
669
701
|
await mkdir2(targetFolder, { recursive: true });
|
|
670
702
|
process.chdir(targetFolder);
|
|
703
|
+
let baseSha = null;
|
|
671
704
|
if (isLocalTemplate) {
|
|
672
705
|
progress.step("copying local template");
|
|
673
706
|
const sourcePath = resolve2(originalCwd, templateUrl);
|
|
@@ -681,22 +714,23 @@ async function create({
|
|
|
681
714
|
}
|
|
682
715
|
const tracked = await listTemplateFiles(sourcePath);
|
|
683
716
|
for (const rel2 of tracked) {
|
|
684
|
-
const src =
|
|
717
|
+
const src = join5(sourcePath, rel2);
|
|
685
718
|
if (!existsSync(src)) continue;
|
|
686
|
-
const dest =
|
|
687
|
-
await mkdir2(
|
|
719
|
+
const dest = join5(targetFolder, rel2);
|
|
720
|
+
await mkdir2(dirname2(dest), { recursive: true });
|
|
688
721
|
await cp(src, dest);
|
|
689
722
|
}
|
|
690
723
|
} else {
|
|
691
724
|
const ref = templateRef ?? "main";
|
|
692
725
|
progress.step(`downloading cella template${templateRef ? ` (${templateRef})` : ""}`);
|
|
693
|
-
await downloadGithubTemplate(template, ref, targetFolder);
|
|
726
|
+
baseSha = await downloadGithubTemplate(template, ref, targetFolder);
|
|
694
727
|
}
|
|
695
728
|
if (!silent) await promptOptionalModules(targetFolder);
|
|
696
729
|
progress.step("cleaning template");
|
|
697
730
|
const displayName = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
698
731
|
await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });
|
|
699
|
-
|
|
732
|
+
if (baseSha) await writeScaffoldBase(targetFolder, baseSha);
|
|
733
|
+
const gitFolderPath = join5(targetFolder, ".git");
|
|
700
734
|
if (!shouldSkipStep("git") && !existsSync(gitFolderPath)) {
|
|
701
735
|
progress.step("initializing git");
|
|
702
736
|
await gitInit(targetFolder);
|
|
@@ -741,14 +775,14 @@ async function promptOptionalModules(targetFolder) {
|
|
|
741
775
|
confirmChoice("Optional modules", keep.length ? keep.join(", ") : pc5.dim("none"));
|
|
742
776
|
resumeSpinner();
|
|
743
777
|
const folders = modules.filter((m) => !keep.includes(m.name)).flatMap(optionalModuleFolders);
|
|
744
|
-
await Promise.all(folders.map((f) =>
|
|
778
|
+
await Promise.all(folders.map((f) => rm2(join5(targetFolder, f), { recursive: true, force: true })));
|
|
745
779
|
}
|
|
746
780
|
|
|
747
781
|
// src/utils/detect-used-ports.ts
|
|
748
782
|
import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
|
|
749
|
-
import { dirname as
|
|
783
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
750
784
|
async function detectUsedPorts(targetFolder) {
|
|
751
|
-
const parentDir =
|
|
785
|
+
const parentDir = dirname3(targetFolder);
|
|
752
786
|
const used = [];
|
|
753
787
|
let siblings;
|
|
754
788
|
try {
|
|
@@ -757,7 +791,7 @@ async function detectUsedPorts(targetFolder) {
|
|
|
757
791
|
return used;
|
|
758
792
|
}
|
|
759
793
|
for (const name of siblings) {
|
|
760
|
-
const configPath =
|
|
794
|
+
const configPath = join6(parentDir, name, "shared/config/config.development.ts");
|
|
761
795
|
try {
|
|
762
796
|
const content = await readFile3(configPath, "utf8");
|
|
763
797
|
const feMatch = content.match(/frontendUrl:\s*'http:\/\/localhost:(\d+)'/);
|
|
@@ -785,22 +819,6 @@ function findNextOffset(usedPorts) {
|
|
|
785
819
|
return 0;
|
|
786
820
|
}
|
|
787
821
|
|
|
788
|
-
// src/utils/extract-package-json-version-from-uri.ts
|
|
789
|
-
async function extractPackageJsonVersionFromUri(repositoryUrl, branch = "main") {
|
|
790
|
-
const [owner, repo] = repositoryUrl.replace("github:", "").split("/");
|
|
791
|
-
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/package.json?ref=${branch}`;
|
|
792
|
-
try {
|
|
793
|
-
const response = await fetch(apiUrl, {
|
|
794
|
-
headers: { Accept: "application/vnd.github.raw+json" }
|
|
795
|
-
});
|
|
796
|
-
if (!response.ok) return "unknown";
|
|
797
|
-
const packageJson = await response.json();
|
|
798
|
-
return packageJson.version || "unknown";
|
|
799
|
-
} catch {
|
|
800
|
-
return "unknown";
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
822
|
// src/utils/fetch-template-metadata.ts
|
|
805
823
|
function parseRepo(repositoryUrl) {
|
|
806
824
|
const [owner, repo] = repositoryUrl.replace("github:", "").split("/");
|
|
@@ -817,7 +835,7 @@ async function fetchLatestRelease(repositoryUrl) {
|
|
|
817
835
|
const response = await fetch(apiUrl, { headers: GITHUB_HEADERS });
|
|
818
836
|
if (!response.ok) return null;
|
|
819
837
|
const releases = await response.json();
|
|
820
|
-
const release = releases.find((r) => !r.draft && !r.prerelease && /^v
|
|
838
|
+
const release = releases.find((r) => !r.draft && !r.prerelease && /^v?\d/.test(r.tag_name ?? ""));
|
|
821
839
|
if (!release?.tag_name) return null;
|
|
822
840
|
return { tag: release.tag_name, date: toDate(release.published_at) };
|
|
823
841
|
} catch {
|
|
@@ -848,8 +866,7 @@ async function isEmptyDirectory(path2) {
|
|
|
848
866
|
|
|
849
867
|
// src/create-cella-cli.ts
|
|
850
868
|
async function main() {
|
|
851
|
-
|
|
852
|
-
showWelcome(templateVersion);
|
|
869
|
+
showWelcome();
|
|
853
870
|
const promptTheme = { prefix: "", style: { answer: (text) => text } };
|
|
854
871
|
const promptContext = { clearPromptOnDone: true };
|
|
855
872
|
const templateRef = cli.options.template ? void 0 : await promptTemplateRef(promptTheme, promptContext);
|
|
@@ -878,7 +895,7 @@ async function main() {
|
|
|
878
895
|
theme: promptTheme,
|
|
879
896
|
choices: [
|
|
880
897
|
{ name: "Cancel and exit", value: "cancel" },
|
|
881
|
-
{ name: "
|
|
898
|
+
{ name: "Remove existing files and continue", value: "remove" }
|
|
882
899
|
]
|
|
883
900
|
},
|
|
884
901
|
promptContext
|
|
@@ -886,6 +903,9 @@ async function main() {
|
|
|
886
903
|
if (action === "cancel") {
|
|
887
904
|
process.exit(1);
|
|
888
905
|
}
|
|
906
|
+
if (action === "remove") {
|
|
907
|
+
await rm3(targetFolder, { recursive: true, force: true });
|
|
908
|
+
}
|
|
889
909
|
}
|
|
890
910
|
const portOffset = cli.options.portOffset ?? await promptPortOffset(targetFolder, promptTheme, promptContext);
|
|
891
911
|
confirmChoice("Port offset", formatOffset(portOffset));
|
|
@@ -925,8 +945,12 @@ async function promptTemplateRef(theme, context) {
|
|
|
925
945
|
console.info(` ${pc6.gray(`https://github.com/${repo}/commit/${commit.sha}`)}`);
|
|
926
946
|
}
|
|
927
947
|
console.info();
|
|
928
|
-
if (!release) return
|
|
948
|
+
if (!release) return "main";
|
|
929
949
|
if (!commit) return release.tag;
|
|
950
|
+
if (!process.stdin.isTTY) {
|
|
951
|
+
confirmChoice("Latest release", release.tag);
|
|
952
|
+
return release.tag;
|
|
953
|
+
}
|
|
930
954
|
const choice = await select(
|
|
931
955
|
{
|
|
932
956
|
message: "Start from",
|
|
@@ -943,7 +967,7 @@ async function promptTemplateRef(theme, context) {
|
|
|
943
967
|
return release.tag;
|
|
944
968
|
}
|
|
945
969
|
confirmChoice("Latest commit", commit.shortSha);
|
|
946
|
-
return
|
|
970
|
+
return "main";
|
|
947
971
|
}
|
|
948
972
|
function formatOffset(o, suffix = "") {
|
|
949
973
|
return `${o} \u2192 :${3e3 + o} / :${4e3 + o} / :${5432 + o}${suffix}`;
|