create-lacspace-app 2.8.0 → 2.9.0
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 +5 -0
- package/dist/index.js +135 -4
- package/dist/lib.cjs +135 -4
- package/dist/lib.js +135 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,11 @@ npx create-lacspace-app my-app --template saas --fullstack
|
|
|
20
20
|
|
|
21
21
|
You choose the *kind* of site you're building. It writes a **real Next.js 15 + React 19 + Tailwind v4 app** — not a hello-world, but a genuinely **polished, modern site**: a fluid `clamp()` type scale, tight display headings, a refined light **and** dark palette, glass chrome, soft layered shadows, a smooth logo marquee, animated counters, scroll reveals and a shimmering primary CTA — every page filled in, an SEO stack wired end-to-end, and a **26-component UI kit** you can drop in anywhere.
|
|
22
22
|
|
|
23
|
+
> **New in v2.9 — guided & discoverable.** Made for newcomers:
|
|
24
|
+
> - **Guided start** — the interactive prompt now offers a **recipe** first ("what are you building?"), so you can ship a whole product without knowing the flags.
|
|
25
|
+
> - **`list`** — `npx create-lacspace-app list` shows every template, add-on and recipe. **`explain <name>`** describes what any add-on or recipe gives you (packages, next steps, docs link).
|
|
26
|
+
> - **`--dry-run`** prints the exact files a command would create without writing anything. `--brand "#hex"` is an alias for `--theme`.
|
|
27
|
+
|
|
23
28
|
> **New in v2.8 — recipes.** Scaffold a whole *kind of product* in one command with `--recipe <key>` (a curated template + full-stack mode + add-on stack):
|
|
24
29
|
> - **`ai-saas`** → SaaS + accounts + payments + AI chat + analytics
|
|
25
30
|
> - **`store`** → e-commerce + eSewa/Khalti checkout + email + analytics
|
package/dist/index.js
CHANGED
|
@@ -7217,7 +7217,7 @@ function backendFiles(ctx) {
|
|
|
7217
7217
|
}
|
|
7218
7218
|
var splitList = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
7219
7219
|
function parseArgs(list) {
|
|
7220
|
-
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false };
|
|
7220
|
+
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false, dryRun: false };
|
|
7221
7221
|
for (let i = 0; i < list.length; i++) {
|
|
7222
7222
|
const arg = list[i];
|
|
7223
7223
|
const next = () => list[++i] ?? "";
|
|
@@ -7225,8 +7225,9 @@ function parseArgs(list) {
|
|
|
7225
7225
|
else if (arg === "-y" || arg === "--yes") a.yes = true;
|
|
7226
7226
|
else if (arg === "--no-install") a.install = false;
|
|
7227
7227
|
else if (arg === "--no-git") a.git = false;
|
|
7228
|
+
else if (arg === "--dry-run" || arg === "--dry") a.dryRun = true;
|
|
7228
7229
|
else if (arg === "--pm") a.pm = next();
|
|
7229
|
-
else if (arg === "--theme" || arg === "--accent") a.theme = next();
|
|
7230
|
+
else if (arg === "--theme" || arg === "--accent" || arg === "--brand") a.theme = next();
|
|
7230
7231
|
else if (arg === "--with" || arg === "--features") a.features.push(...splitList(next()));
|
|
7231
7232
|
else if (arg === "--fullstack" || arg === "--full-stack" || arg === "--dynamic") a.mode = "dynamic";
|
|
7232
7233
|
else if (arg === "--static") a.mode = "static";
|
|
@@ -7242,6 +7243,7 @@ function parseArgs(list) {
|
|
|
7242
7243
|
else if (arg.startsWith("--template=")) a.template = arg.slice(11);
|
|
7243
7244
|
else if (arg.startsWith("--theme=")) a.theme = arg.slice(8);
|
|
7244
7245
|
else if (arg.startsWith("--accent=")) a.theme = arg.slice(9);
|
|
7246
|
+
else if (arg.startsWith("--brand=")) a.theme = arg.slice(8);
|
|
7245
7247
|
else if (arg.startsWith("--with=")) a.features.push(...splitList(arg.slice(7)));
|
|
7246
7248
|
else if (arg.startsWith("--features=")) a.features.push(...splitList(arg.slice(11)));
|
|
7247
7249
|
else if (!arg.startsWith("-") && !a.name) a.name = arg;
|
|
@@ -7332,6 +7334,8 @@ ${c("bold", "Usage")}
|
|
|
7332
7334
|
npm create lacspace-app@latest <name> [options]
|
|
7333
7335
|
npx create-lacspace-app <name> --template <key>
|
|
7334
7336
|
npx create-lacspace-app add <section...> ${c("dim", "# grow an existing app")}
|
|
7337
|
+
npx create-lacspace-app list ${c("dim", "# templates, add-ons & recipes")}
|
|
7338
|
+
npx create-lacspace-app explain <name> ${c("dim", "# what an add-on/recipe gives you")}
|
|
7335
7339
|
|
|
7336
7340
|
${c("bold", "Templates")}
|
|
7337
7341
|
${TEMPLATES.map((t) => ` ${t.key.padEnd(10)} ${t.description}`).join("\n")}
|
|
@@ -7343,8 +7347,9 @@ ${c("bold", "Options")}
|
|
|
7343
7347
|
${c("dim", "(alias --dynamic; default is --static, a single Next.js app)")}
|
|
7344
7348
|
--with <a,b> Feature add-ons, comma-separated (alias --features)
|
|
7345
7349
|
--recipe <key> Start from a curated stack (${RECIPES.map((r) => r.key).join(" | ")})
|
|
7346
|
-
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (
|
|
7350
|
+
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (alias --brand)
|
|
7347
7351
|
--pm <npm|pnpm|yarn|bun> Package manager (default npm)
|
|
7352
|
+
--dry-run Print the files that would be created; write nothing
|
|
7348
7353
|
--no-install Skip installing dependencies
|
|
7349
7354
|
--no-git Skip git init
|
|
7350
7355
|
-y, --yes Accept defaults (needs <name>)
|
|
@@ -7720,12 +7725,103 @@ ${c("bold", "Use them")} \u2014 import into any page:
|
|
|
7720
7725
|
return;
|
|
7721
7726
|
}
|
|
7722
7727
|
}
|
|
7728
|
+
function runList() {
|
|
7729
|
+
stdout.write(`
|
|
7730
|
+
${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 what you can build")}
|
|
7731
|
+
`);
|
|
7732
|
+
stdout.write(`
|
|
7733
|
+
${c("bold", "Templates")} ${c("dim", "(-t / --template)")}
|
|
7734
|
+
`);
|
|
7735
|
+
for (const t of TEMPLATES) stdout.write(` ${c("cyan", t.key.padEnd(12))} ${c("dim", t.description)}
|
|
7736
|
+
`);
|
|
7737
|
+
stdout.write(`
|
|
7738
|
+
${c("bold", "Add-ons")} ${c("dim", "(--with) \u2014 free & keyless")}
|
|
7739
|
+
`);
|
|
7740
|
+
for (const f of FEATURES) stdout.write(` ${c("cyan", f.key.padEnd(12))} ${c("dim", f.description)}${f.requiresBackend ? c("green", " [full-stack]") : ""}
|
|
7741
|
+
`);
|
|
7742
|
+
stdout.write(`
|
|
7743
|
+
${c("bold", "Recipes")} ${c("dim", "(--recipe) \u2014 a whole product in one command")}
|
|
7744
|
+
`);
|
|
7745
|
+
for (const r of RECIPES) stdout.write(` ${c("cyan", r.key.padEnd(14))} ${c("dim", r.description)}
|
|
7746
|
+
`);
|
|
7747
|
+
stdout.write(`
|
|
7748
|
+
${c("dim", "Learn one:")} ${c("cyan", "npx create-lacspace-app explain <name>")}
|
|
7749
|
+
|
|
7750
|
+
`);
|
|
7751
|
+
}
|
|
7752
|
+
function runExplain(rawNames) {
|
|
7753
|
+
const name = rawNames.find((n) => !n.startsWith("-"))?.toLowerCase();
|
|
7754
|
+
if (!name) {
|
|
7755
|
+
stdout.write(`Usage: ${c("cyan", "npx create-lacspace-app explain <template|add-on|recipe>")}
|
|
7756
|
+
|
|
7757
|
+
`);
|
|
7758
|
+
runList();
|
|
7759
|
+
return;
|
|
7760
|
+
}
|
|
7761
|
+
const feat = FEATURES.find((f) => f.key === name);
|
|
7762
|
+
const recipe = RECIPES.find((r) => r.key === name);
|
|
7763
|
+
const tmpl = TEMPLATES.find((t) => t.key === name);
|
|
7764
|
+
stdout.write("\n");
|
|
7765
|
+
if (feat) {
|
|
7766
|
+
stdout.write(`${c("bold", c("magenta", "add-on: " + feat.key))} ${feat.requiresBackend ? c("green", "[full-stack]") : c("dim", "[frontend]")}
|
|
7767
|
+
|
|
7768
|
+
${feat.description}
|
|
7769
|
+
`);
|
|
7770
|
+
const deps = { ...feat.deps, ...feat.backend?.({ name: "app", template: TEMPLATES[0], features: [], mode: "dynamic" })?.deps ?? {} };
|
|
7771
|
+
if (Object.keys(deps).length) stdout.write(`
|
|
7772
|
+
${c("bold", "Packages")}: ${Object.keys(deps).map((d) => c("cyan", d)).join(", ")}
|
|
7773
|
+
`);
|
|
7774
|
+
stdout.write(`
|
|
7775
|
+
${c("bold", "Next steps")}:
|
|
7776
|
+
`);
|
|
7777
|
+
for (const s of feat.nextSteps) stdout.write(` ${c("dim", "\u2022")} ${s}
|
|
7778
|
+
`);
|
|
7779
|
+
if (feat.learn) stdout.write(`
|
|
7780
|
+
Learn more \u2192 ${c("cyan", feat.learn)}
|
|
7781
|
+
`);
|
|
7782
|
+
stdout.write(`
|
|
7783
|
+
Add it: ${c("cyan", "npx create-lacspace-app my-app --with " + feat.key)}
|
|
7784
|
+
|
|
7785
|
+
`);
|
|
7786
|
+
} else if (recipe) {
|
|
7787
|
+
stdout.write(`${c("bold", c("magenta", "recipe: " + recipe.key))}
|
|
7788
|
+
|
|
7789
|
+
${recipe.description}
|
|
7790
|
+
|
|
7791
|
+
${c("bold", "Template")}: ${c("cyan", recipe.template)}${recipe.mode ? c("dim", " \xB7 " + recipe.mode) : ""}
|
|
7792
|
+
${c("bold", "Add-ons")}: ${recipe.features.map((f) => c("cyan", f)).join(", ")}
|
|
7793
|
+
|
|
7794
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --recipe " + recipe.key)}
|
|
7795
|
+
|
|
7796
|
+
`);
|
|
7797
|
+
} else if (tmpl) {
|
|
7798
|
+
stdout.write(`${c("bold", c("magenta", "template: " + tmpl.key))}
|
|
7799
|
+
|
|
7800
|
+
${tmpl.description}
|
|
7801
|
+
|
|
7802
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --template " + tmpl.key)}
|
|
7803
|
+
|
|
7804
|
+
`);
|
|
7805
|
+
} else {
|
|
7806
|
+
stdout.write(c("yellow", `Unknown "${name}".
|
|
7807
|
+
`));
|
|
7808
|
+
runList();
|
|
7809
|
+
}
|
|
7810
|
+
}
|
|
7723
7811
|
async function main() {
|
|
7724
7812
|
const raw = argv.slice(2);
|
|
7725
7813
|
if (raw[0] === "add") {
|
|
7726
7814
|
runAdd(raw.slice(1));
|
|
7727
7815
|
return;
|
|
7728
7816
|
}
|
|
7817
|
+
if (raw[0] === "list" || raw[0] === "ls") {
|
|
7818
|
+
runList();
|
|
7819
|
+
return;
|
|
7820
|
+
}
|
|
7821
|
+
if (raw[0] === "explain" || raw[0] === "info") {
|
|
7822
|
+
runExplain(raw.slice(1));
|
|
7823
|
+
return;
|
|
7824
|
+
}
|
|
7729
7825
|
const args = parseArgs(raw);
|
|
7730
7826
|
if (args.help) {
|
|
7731
7827
|
stdout.write(HELP + "\n");
|
|
@@ -7744,10 +7840,32 @@ ${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 a go
|
|
|
7744
7840
|
let templateKey = args.template ?? recipe?.template;
|
|
7745
7841
|
let mode = args.mode ?? recipe?.mode ?? "static";
|
|
7746
7842
|
const featureKeys = [...recipe?.features ?? [], ...args.features];
|
|
7843
|
+
let usedRecipe = Boolean(recipe);
|
|
7747
7844
|
if (!args.yes && stdin.isTTY) {
|
|
7748
7845
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
7749
7846
|
try {
|
|
7750
7847
|
if (!name) name = (await rl.question(`${c("green", "?")} Project name ${c("dim", "(my-app)")}: `)).trim() || "my-app";
|
|
7848
|
+
if (!usedRecipe && !templateKey && featureKeys.length === 0) {
|
|
7849
|
+
stdout.write(`
|
|
7850
|
+
Start from a recipe? ${c("dim", "(a template + add-ons, ready to go)")}
|
|
7851
|
+
`);
|
|
7852
|
+
RECIPES.forEach((r, i) => stdout.write(` ${c("cyan", String(i + 1))}. ${c("bold", r.label)} ${c("dim", "\u2014 " + r.description)}
|
|
7853
|
+
`));
|
|
7854
|
+
stdout.write(` ${c("cyan", "0")}. ${c("dim", "No recipe \u2014 I'll choose a template myself")}
|
|
7855
|
+
`);
|
|
7856
|
+
const ans = (await rl.question(`
|
|
7857
|
+
${c("green", "?")} Recipe ${c("dim", "(0)")}: `)).trim() || "0";
|
|
7858
|
+
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : RECIPES.findIndex((r) => r.key === ans);
|
|
7859
|
+
const chosen = ans === "0" ? void 0 : RECIPES[idx];
|
|
7860
|
+
if (chosen) {
|
|
7861
|
+
usedRecipe = true;
|
|
7862
|
+
templateKey = chosen.template;
|
|
7863
|
+
mode = chosen.mode ?? mode;
|
|
7864
|
+
for (const k of chosen.features) if (!featureKeys.includes(k)) featureKeys.push(k);
|
|
7865
|
+
stdout.write(` ${c("green", "\u2714")} Recipe ${c("cyan", chosen.key)}
|
|
7866
|
+
`);
|
|
7867
|
+
}
|
|
7868
|
+
}
|
|
7751
7869
|
if (!templateKey) {
|
|
7752
7870
|
stdout.write(`
|
|
7753
7871
|
Choose a template:
|
|
@@ -7759,7 +7877,7 @@ ${c("green", "?")} Template ${c("dim", "(1)")}: `)).trim() || "1";
|
|
|
7759
7877
|
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : TEMPLATES.findIndex((t) => t.key === ans);
|
|
7760
7878
|
templateKey = TEMPLATES[idx]?.key ?? "personal";
|
|
7761
7879
|
}
|
|
7762
|
-
if (args.mode === void 0 && !
|
|
7880
|
+
if (args.mode === void 0 && !usedRecipe) {
|
|
7763
7881
|
stdout.write(`
|
|
7764
7882
|
What kind of app?
|
|
7765
7883
|
`);
|
|
@@ -7818,6 +7936,19 @@ ${c("green", "?")} Add features? ${c("dim", "(comma-separated numbers, or Enter
|
|
|
7818
7936
|
`);
|
|
7819
7937
|
}
|
|
7820
7938
|
const files = buildFiles({ name: projectName, template, features, mode });
|
|
7939
|
+
if (args.dryRun) {
|
|
7940
|
+
const keys = Object.keys(files).sort();
|
|
7941
|
+
stdout.write(`
|
|
7942
|
+
${c("bold", "Dry run")} ${c("dim", `\u2014 ${keys.length} files (${mode}${features.length ? ", +" + features.map((f) => f.key).join(",") : ""}), nothing written`)}
|
|
7943
|
+
`);
|
|
7944
|
+
for (const rel of keys) stdout.write(` ${c("dim", "+ " + rel)}
|
|
7945
|
+
`);
|
|
7946
|
+
stdout.write(`
|
|
7947
|
+
${c("dim", "Remove --dry-run to create the project.")}
|
|
7948
|
+
|
|
7949
|
+
`);
|
|
7950
|
+
return;
|
|
7951
|
+
}
|
|
7821
7952
|
for (const [rel, content] of Object.entries(files)) {
|
|
7822
7953
|
const full = join(dir, rel);
|
|
7823
7954
|
mkdirSync(dirname(full), { recursive: true });
|
package/dist/lib.cjs
CHANGED
|
@@ -7221,7 +7221,7 @@ function backendFiles(ctx) {
|
|
|
7221
7221
|
}
|
|
7222
7222
|
var splitList = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
7223
7223
|
function parseArgs(list) {
|
|
7224
|
-
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false };
|
|
7224
|
+
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false, dryRun: false };
|
|
7225
7225
|
for (let i = 0; i < list.length; i++) {
|
|
7226
7226
|
const arg = list[i];
|
|
7227
7227
|
const next = () => list[++i] ?? "";
|
|
@@ -7229,8 +7229,9 @@ function parseArgs(list) {
|
|
|
7229
7229
|
else if (arg === "-y" || arg === "--yes") a.yes = true;
|
|
7230
7230
|
else if (arg === "--no-install") a.install = false;
|
|
7231
7231
|
else if (arg === "--no-git") a.git = false;
|
|
7232
|
+
else if (arg === "--dry-run" || arg === "--dry") a.dryRun = true;
|
|
7232
7233
|
else if (arg === "--pm") a.pm = next();
|
|
7233
|
-
else if (arg === "--theme" || arg === "--accent") a.theme = next();
|
|
7234
|
+
else if (arg === "--theme" || arg === "--accent" || arg === "--brand") a.theme = next();
|
|
7234
7235
|
else if (arg === "--with" || arg === "--features") a.features.push(...splitList(next()));
|
|
7235
7236
|
else if (arg === "--fullstack" || arg === "--full-stack" || arg === "--dynamic") a.mode = "dynamic";
|
|
7236
7237
|
else if (arg === "--static") a.mode = "static";
|
|
@@ -7246,6 +7247,7 @@ function parseArgs(list) {
|
|
|
7246
7247
|
else if (arg.startsWith("--template=")) a.template = arg.slice(11);
|
|
7247
7248
|
else if (arg.startsWith("--theme=")) a.theme = arg.slice(8);
|
|
7248
7249
|
else if (arg.startsWith("--accent=")) a.theme = arg.slice(9);
|
|
7250
|
+
else if (arg.startsWith("--brand=")) a.theme = arg.slice(8);
|
|
7249
7251
|
else if (arg.startsWith("--with=")) a.features.push(...splitList(arg.slice(7)));
|
|
7250
7252
|
else if (arg.startsWith("--features=")) a.features.push(...splitList(arg.slice(11)));
|
|
7251
7253
|
else if (!arg.startsWith("-") && !a.name) a.name = arg;
|
|
@@ -7336,6 +7338,8 @@ ${c("bold", "Usage")}
|
|
|
7336
7338
|
npm create lacspace-app@latest <name> [options]
|
|
7337
7339
|
npx create-lacspace-app <name> --template <key>
|
|
7338
7340
|
npx create-lacspace-app add <section...> ${c("dim", "# grow an existing app")}
|
|
7341
|
+
npx create-lacspace-app list ${c("dim", "# templates, add-ons & recipes")}
|
|
7342
|
+
npx create-lacspace-app explain <name> ${c("dim", "# what an add-on/recipe gives you")}
|
|
7339
7343
|
|
|
7340
7344
|
${c("bold", "Templates")}
|
|
7341
7345
|
${TEMPLATES.map((t) => ` ${t.key.padEnd(10)} ${t.description}`).join("\n")}
|
|
@@ -7347,8 +7351,9 @@ ${c("bold", "Options")}
|
|
|
7347
7351
|
${c("dim", "(alias --dynamic; default is --static, a single Next.js app)")}
|
|
7348
7352
|
--with <a,b> Feature add-ons, comma-separated (alias --features)
|
|
7349
7353
|
--recipe <key> Start from a curated stack (${RECIPES.map((r) => r.key).join(" | ")})
|
|
7350
|
-
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (
|
|
7354
|
+
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (alias --brand)
|
|
7351
7355
|
--pm <npm|pnpm|yarn|bun> Package manager (default npm)
|
|
7356
|
+
--dry-run Print the files that would be created; write nothing
|
|
7352
7357
|
--no-install Skip installing dependencies
|
|
7353
7358
|
--no-git Skip git init
|
|
7354
7359
|
-y, --yes Accept defaults (needs <name>)
|
|
@@ -7724,12 +7729,103 @@ ${c("bold", "Use them")} \u2014 import into any page:
|
|
|
7724
7729
|
return;
|
|
7725
7730
|
}
|
|
7726
7731
|
}
|
|
7732
|
+
function runList() {
|
|
7733
|
+
process$1.stdout.write(`
|
|
7734
|
+
${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 what you can build")}
|
|
7735
|
+
`);
|
|
7736
|
+
process$1.stdout.write(`
|
|
7737
|
+
${c("bold", "Templates")} ${c("dim", "(-t / --template)")}
|
|
7738
|
+
`);
|
|
7739
|
+
for (const t of TEMPLATES) process$1.stdout.write(` ${c("cyan", t.key.padEnd(12))} ${c("dim", t.description)}
|
|
7740
|
+
`);
|
|
7741
|
+
process$1.stdout.write(`
|
|
7742
|
+
${c("bold", "Add-ons")} ${c("dim", "(--with) \u2014 free & keyless")}
|
|
7743
|
+
`);
|
|
7744
|
+
for (const f of FEATURES) process$1.stdout.write(` ${c("cyan", f.key.padEnd(12))} ${c("dim", f.description)}${f.requiresBackend ? c("green", " [full-stack]") : ""}
|
|
7745
|
+
`);
|
|
7746
|
+
process$1.stdout.write(`
|
|
7747
|
+
${c("bold", "Recipes")} ${c("dim", "(--recipe) \u2014 a whole product in one command")}
|
|
7748
|
+
`);
|
|
7749
|
+
for (const r of RECIPES) process$1.stdout.write(` ${c("cyan", r.key.padEnd(14))} ${c("dim", r.description)}
|
|
7750
|
+
`);
|
|
7751
|
+
process$1.stdout.write(`
|
|
7752
|
+
${c("dim", "Learn one:")} ${c("cyan", "npx create-lacspace-app explain <name>")}
|
|
7753
|
+
|
|
7754
|
+
`);
|
|
7755
|
+
}
|
|
7756
|
+
function runExplain(rawNames) {
|
|
7757
|
+
const name = rawNames.find((n) => !n.startsWith("-"))?.toLowerCase();
|
|
7758
|
+
if (!name) {
|
|
7759
|
+
process$1.stdout.write(`Usage: ${c("cyan", "npx create-lacspace-app explain <template|add-on|recipe>")}
|
|
7760
|
+
|
|
7761
|
+
`);
|
|
7762
|
+
runList();
|
|
7763
|
+
return;
|
|
7764
|
+
}
|
|
7765
|
+
const feat = FEATURES.find((f) => f.key === name);
|
|
7766
|
+
const recipe = RECIPES.find((r) => r.key === name);
|
|
7767
|
+
const tmpl = TEMPLATES.find((t) => t.key === name);
|
|
7768
|
+
process$1.stdout.write("\n");
|
|
7769
|
+
if (feat) {
|
|
7770
|
+
process$1.stdout.write(`${c("bold", c("magenta", "add-on: " + feat.key))} ${feat.requiresBackend ? c("green", "[full-stack]") : c("dim", "[frontend]")}
|
|
7771
|
+
|
|
7772
|
+
${feat.description}
|
|
7773
|
+
`);
|
|
7774
|
+
const deps = { ...feat.deps, ...feat.backend?.({ name: "app", template: TEMPLATES[0], features: [], mode: "dynamic" })?.deps ?? {} };
|
|
7775
|
+
if (Object.keys(deps).length) process$1.stdout.write(`
|
|
7776
|
+
${c("bold", "Packages")}: ${Object.keys(deps).map((d) => c("cyan", d)).join(", ")}
|
|
7777
|
+
`);
|
|
7778
|
+
process$1.stdout.write(`
|
|
7779
|
+
${c("bold", "Next steps")}:
|
|
7780
|
+
`);
|
|
7781
|
+
for (const s of feat.nextSteps) process$1.stdout.write(` ${c("dim", "\u2022")} ${s}
|
|
7782
|
+
`);
|
|
7783
|
+
if (feat.learn) process$1.stdout.write(`
|
|
7784
|
+
Learn more \u2192 ${c("cyan", feat.learn)}
|
|
7785
|
+
`);
|
|
7786
|
+
process$1.stdout.write(`
|
|
7787
|
+
Add it: ${c("cyan", "npx create-lacspace-app my-app --with " + feat.key)}
|
|
7788
|
+
|
|
7789
|
+
`);
|
|
7790
|
+
} else if (recipe) {
|
|
7791
|
+
process$1.stdout.write(`${c("bold", c("magenta", "recipe: " + recipe.key))}
|
|
7792
|
+
|
|
7793
|
+
${recipe.description}
|
|
7794
|
+
|
|
7795
|
+
${c("bold", "Template")}: ${c("cyan", recipe.template)}${recipe.mode ? c("dim", " \xB7 " + recipe.mode) : ""}
|
|
7796
|
+
${c("bold", "Add-ons")}: ${recipe.features.map((f) => c("cyan", f)).join(", ")}
|
|
7797
|
+
|
|
7798
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --recipe " + recipe.key)}
|
|
7799
|
+
|
|
7800
|
+
`);
|
|
7801
|
+
} else if (tmpl) {
|
|
7802
|
+
process$1.stdout.write(`${c("bold", c("magenta", "template: " + tmpl.key))}
|
|
7803
|
+
|
|
7804
|
+
${tmpl.description}
|
|
7805
|
+
|
|
7806
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --template " + tmpl.key)}
|
|
7807
|
+
|
|
7808
|
+
`);
|
|
7809
|
+
} else {
|
|
7810
|
+
process$1.stdout.write(c("yellow", `Unknown "${name}".
|
|
7811
|
+
`));
|
|
7812
|
+
runList();
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7727
7815
|
async function main() {
|
|
7728
7816
|
const raw = process$1.argv.slice(2);
|
|
7729
7817
|
if (raw[0] === "add") {
|
|
7730
7818
|
runAdd(raw.slice(1));
|
|
7731
7819
|
return;
|
|
7732
7820
|
}
|
|
7821
|
+
if (raw[0] === "list" || raw[0] === "ls") {
|
|
7822
|
+
runList();
|
|
7823
|
+
return;
|
|
7824
|
+
}
|
|
7825
|
+
if (raw[0] === "explain" || raw[0] === "info") {
|
|
7826
|
+
runExplain(raw.slice(1));
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7733
7829
|
const args = parseArgs(raw);
|
|
7734
7830
|
if (args.help) {
|
|
7735
7831
|
process$1.stdout.write(HELP + "\n");
|
|
@@ -7748,10 +7844,32 @@ ${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 a go
|
|
|
7748
7844
|
let templateKey = args.template ?? recipe?.template;
|
|
7749
7845
|
let mode = args.mode ?? recipe?.mode ?? "static";
|
|
7750
7846
|
const featureKeys = [...recipe?.features ?? [], ...args.features];
|
|
7847
|
+
let usedRecipe = Boolean(recipe);
|
|
7751
7848
|
if (!args.yes && process$1.stdin.isTTY) {
|
|
7752
7849
|
const rl = promises.createInterface({ input: process$1.stdin, output: process$1.stdout });
|
|
7753
7850
|
try {
|
|
7754
7851
|
if (!name) name = (await rl.question(`${c("green", "?")} Project name ${c("dim", "(my-app)")}: `)).trim() || "my-app";
|
|
7852
|
+
if (!usedRecipe && !templateKey && featureKeys.length === 0) {
|
|
7853
|
+
process$1.stdout.write(`
|
|
7854
|
+
Start from a recipe? ${c("dim", "(a template + add-ons, ready to go)")}
|
|
7855
|
+
`);
|
|
7856
|
+
RECIPES.forEach((r, i) => process$1.stdout.write(` ${c("cyan", String(i + 1))}. ${c("bold", r.label)} ${c("dim", "\u2014 " + r.description)}
|
|
7857
|
+
`));
|
|
7858
|
+
process$1.stdout.write(` ${c("cyan", "0")}. ${c("dim", "No recipe \u2014 I'll choose a template myself")}
|
|
7859
|
+
`);
|
|
7860
|
+
const ans = (await rl.question(`
|
|
7861
|
+
${c("green", "?")} Recipe ${c("dim", "(0)")}: `)).trim() || "0";
|
|
7862
|
+
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : RECIPES.findIndex((r) => r.key === ans);
|
|
7863
|
+
const chosen = ans === "0" ? void 0 : RECIPES[idx];
|
|
7864
|
+
if (chosen) {
|
|
7865
|
+
usedRecipe = true;
|
|
7866
|
+
templateKey = chosen.template;
|
|
7867
|
+
mode = chosen.mode ?? mode;
|
|
7868
|
+
for (const k of chosen.features) if (!featureKeys.includes(k)) featureKeys.push(k);
|
|
7869
|
+
process$1.stdout.write(` ${c("green", "\u2714")} Recipe ${c("cyan", chosen.key)}
|
|
7870
|
+
`);
|
|
7871
|
+
}
|
|
7872
|
+
}
|
|
7755
7873
|
if (!templateKey) {
|
|
7756
7874
|
process$1.stdout.write(`
|
|
7757
7875
|
Choose a template:
|
|
@@ -7763,7 +7881,7 @@ ${c("green", "?")} Template ${c("dim", "(1)")}: `)).trim() || "1";
|
|
|
7763
7881
|
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : TEMPLATES.findIndex((t) => t.key === ans);
|
|
7764
7882
|
templateKey = TEMPLATES[idx]?.key ?? "personal";
|
|
7765
7883
|
}
|
|
7766
|
-
if (args.mode === void 0 && !
|
|
7884
|
+
if (args.mode === void 0 && !usedRecipe) {
|
|
7767
7885
|
process$1.stdout.write(`
|
|
7768
7886
|
What kind of app?
|
|
7769
7887
|
`);
|
|
@@ -7822,6 +7940,19 @@ ${c("green", "?")} Add features? ${c("dim", "(comma-separated numbers, or Enter
|
|
|
7822
7940
|
`);
|
|
7823
7941
|
}
|
|
7824
7942
|
const files = buildFiles({ name: projectName, template, features, mode });
|
|
7943
|
+
if (args.dryRun) {
|
|
7944
|
+
const keys = Object.keys(files).sort();
|
|
7945
|
+
process$1.stdout.write(`
|
|
7946
|
+
${c("bold", "Dry run")} ${c("dim", `\u2014 ${keys.length} files (${mode}${features.length ? ", +" + features.map((f) => f.key).join(",") : ""}), nothing written`)}
|
|
7947
|
+
`);
|
|
7948
|
+
for (const rel of keys) process$1.stdout.write(` ${c("dim", "+ " + rel)}
|
|
7949
|
+
`);
|
|
7950
|
+
process$1.stdout.write(`
|
|
7951
|
+
${c("dim", "Remove --dry-run to create the project.")}
|
|
7952
|
+
|
|
7953
|
+
`);
|
|
7954
|
+
return;
|
|
7955
|
+
}
|
|
7825
7956
|
for (const [rel, content] of Object.entries(files)) {
|
|
7826
7957
|
const full = path.join(dir, rel);
|
|
7827
7958
|
fs.mkdirSync(path.dirname(full), { recursive: true });
|
package/dist/lib.js
CHANGED
|
@@ -7218,7 +7218,7 @@ function backendFiles(ctx) {
|
|
|
7218
7218
|
}
|
|
7219
7219
|
var splitList = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
7220
7220
|
function parseArgs(list) {
|
|
7221
|
-
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false };
|
|
7221
|
+
const a = { features: [], yes: false, install: true, git: true, pm: "npm", help: false, dryRun: false };
|
|
7222
7222
|
for (let i = 0; i < list.length; i++) {
|
|
7223
7223
|
const arg = list[i];
|
|
7224
7224
|
const next = () => list[++i] ?? "";
|
|
@@ -7226,8 +7226,9 @@ function parseArgs(list) {
|
|
|
7226
7226
|
else if (arg === "-y" || arg === "--yes") a.yes = true;
|
|
7227
7227
|
else if (arg === "--no-install") a.install = false;
|
|
7228
7228
|
else if (arg === "--no-git") a.git = false;
|
|
7229
|
+
else if (arg === "--dry-run" || arg === "--dry") a.dryRun = true;
|
|
7229
7230
|
else if (arg === "--pm") a.pm = next();
|
|
7230
|
-
else if (arg === "--theme" || arg === "--accent") a.theme = next();
|
|
7231
|
+
else if (arg === "--theme" || arg === "--accent" || arg === "--brand") a.theme = next();
|
|
7231
7232
|
else if (arg === "--with" || arg === "--features") a.features.push(...splitList(next()));
|
|
7232
7233
|
else if (arg === "--fullstack" || arg === "--full-stack" || arg === "--dynamic") a.mode = "dynamic";
|
|
7233
7234
|
else if (arg === "--static") a.mode = "static";
|
|
@@ -7243,6 +7244,7 @@ function parseArgs(list) {
|
|
|
7243
7244
|
else if (arg.startsWith("--template=")) a.template = arg.slice(11);
|
|
7244
7245
|
else if (arg.startsWith("--theme=")) a.theme = arg.slice(8);
|
|
7245
7246
|
else if (arg.startsWith("--accent=")) a.theme = arg.slice(9);
|
|
7247
|
+
else if (arg.startsWith("--brand=")) a.theme = arg.slice(8);
|
|
7246
7248
|
else if (arg.startsWith("--with=")) a.features.push(...splitList(arg.slice(7)));
|
|
7247
7249
|
else if (arg.startsWith("--features=")) a.features.push(...splitList(arg.slice(11)));
|
|
7248
7250
|
else if (!arg.startsWith("-") && !a.name) a.name = arg;
|
|
@@ -7333,6 +7335,8 @@ ${c("bold", "Usage")}
|
|
|
7333
7335
|
npm create lacspace-app@latest <name> [options]
|
|
7334
7336
|
npx create-lacspace-app <name> --template <key>
|
|
7335
7337
|
npx create-lacspace-app add <section...> ${c("dim", "# grow an existing app")}
|
|
7338
|
+
npx create-lacspace-app list ${c("dim", "# templates, add-ons & recipes")}
|
|
7339
|
+
npx create-lacspace-app explain <name> ${c("dim", "# what an add-on/recipe gives you")}
|
|
7336
7340
|
|
|
7337
7341
|
${c("bold", "Templates")}
|
|
7338
7342
|
${TEMPLATES.map((t) => ` ${t.key.padEnd(10)} ${t.description}`).join("\n")}
|
|
@@ -7344,8 +7348,9 @@ ${c("bold", "Options")}
|
|
|
7344
7348
|
${c("dim", "(alias --dynamic; default is --static, a single Next.js app)")}
|
|
7345
7349
|
--with <a,b> Feature add-ons, comma-separated (alias --features)
|
|
7346
7350
|
--recipe <key> Start from a curated stack (${RECIPES.map((r) => r.key).join(" | ")})
|
|
7347
|
-
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (
|
|
7351
|
+
--theme <name|hex> Accent: a preset, a "#hex", or "from,to" (alias --brand)
|
|
7348
7352
|
--pm <npm|pnpm|yarn|bun> Package manager (default npm)
|
|
7353
|
+
--dry-run Print the files that would be created; write nothing
|
|
7349
7354
|
--no-install Skip installing dependencies
|
|
7350
7355
|
--no-git Skip git init
|
|
7351
7356
|
-y, --yes Accept defaults (needs <name>)
|
|
@@ -7721,12 +7726,103 @@ ${c("bold", "Use them")} \u2014 import into any page:
|
|
|
7721
7726
|
return;
|
|
7722
7727
|
}
|
|
7723
7728
|
}
|
|
7729
|
+
function runList() {
|
|
7730
|
+
stdout.write(`
|
|
7731
|
+
${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 what you can build")}
|
|
7732
|
+
`);
|
|
7733
|
+
stdout.write(`
|
|
7734
|
+
${c("bold", "Templates")} ${c("dim", "(-t / --template)")}
|
|
7735
|
+
`);
|
|
7736
|
+
for (const t of TEMPLATES) stdout.write(` ${c("cyan", t.key.padEnd(12))} ${c("dim", t.description)}
|
|
7737
|
+
`);
|
|
7738
|
+
stdout.write(`
|
|
7739
|
+
${c("bold", "Add-ons")} ${c("dim", "(--with) \u2014 free & keyless")}
|
|
7740
|
+
`);
|
|
7741
|
+
for (const f of FEATURES) stdout.write(` ${c("cyan", f.key.padEnd(12))} ${c("dim", f.description)}${f.requiresBackend ? c("green", " [full-stack]") : ""}
|
|
7742
|
+
`);
|
|
7743
|
+
stdout.write(`
|
|
7744
|
+
${c("bold", "Recipes")} ${c("dim", "(--recipe) \u2014 a whole product in one command")}
|
|
7745
|
+
`);
|
|
7746
|
+
for (const r of RECIPES) stdout.write(` ${c("cyan", r.key.padEnd(14))} ${c("dim", r.description)}
|
|
7747
|
+
`);
|
|
7748
|
+
stdout.write(`
|
|
7749
|
+
${c("dim", "Learn one:")} ${c("cyan", "npx create-lacspace-app explain <name>")}
|
|
7750
|
+
|
|
7751
|
+
`);
|
|
7752
|
+
}
|
|
7753
|
+
function runExplain(rawNames) {
|
|
7754
|
+
const name = rawNames.find((n) => !n.startsWith("-"))?.toLowerCase();
|
|
7755
|
+
if (!name) {
|
|
7756
|
+
stdout.write(`Usage: ${c("cyan", "npx create-lacspace-app explain <template|add-on|recipe>")}
|
|
7757
|
+
|
|
7758
|
+
`);
|
|
7759
|
+
runList();
|
|
7760
|
+
return;
|
|
7761
|
+
}
|
|
7762
|
+
const feat = FEATURES.find((f) => f.key === name);
|
|
7763
|
+
const recipe = RECIPES.find((r) => r.key === name);
|
|
7764
|
+
const tmpl = TEMPLATES.find((t) => t.key === name);
|
|
7765
|
+
stdout.write("\n");
|
|
7766
|
+
if (feat) {
|
|
7767
|
+
stdout.write(`${c("bold", c("magenta", "add-on: " + feat.key))} ${feat.requiresBackend ? c("green", "[full-stack]") : c("dim", "[frontend]")}
|
|
7768
|
+
|
|
7769
|
+
${feat.description}
|
|
7770
|
+
`);
|
|
7771
|
+
const deps = { ...feat.deps, ...feat.backend?.({ name: "app", template: TEMPLATES[0], features: [], mode: "dynamic" })?.deps ?? {} };
|
|
7772
|
+
if (Object.keys(deps).length) stdout.write(`
|
|
7773
|
+
${c("bold", "Packages")}: ${Object.keys(deps).map((d) => c("cyan", d)).join(", ")}
|
|
7774
|
+
`);
|
|
7775
|
+
stdout.write(`
|
|
7776
|
+
${c("bold", "Next steps")}:
|
|
7777
|
+
`);
|
|
7778
|
+
for (const s of feat.nextSteps) stdout.write(` ${c("dim", "\u2022")} ${s}
|
|
7779
|
+
`);
|
|
7780
|
+
if (feat.learn) stdout.write(`
|
|
7781
|
+
Learn more \u2192 ${c("cyan", feat.learn)}
|
|
7782
|
+
`);
|
|
7783
|
+
stdout.write(`
|
|
7784
|
+
Add it: ${c("cyan", "npx create-lacspace-app my-app --with " + feat.key)}
|
|
7785
|
+
|
|
7786
|
+
`);
|
|
7787
|
+
} else if (recipe) {
|
|
7788
|
+
stdout.write(`${c("bold", c("magenta", "recipe: " + recipe.key))}
|
|
7789
|
+
|
|
7790
|
+
${recipe.description}
|
|
7791
|
+
|
|
7792
|
+
${c("bold", "Template")}: ${c("cyan", recipe.template)}${recipe.mode ? c("dim", " \xB7 " + recipe.mode) : ""}
|
|
7793
|
+
${c("bold", "Add-ons")}: ${recipe.features.map((f) => c("cyan", f)).join(", ")}
|
|
7794
|
+
|
|
7795
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --recipe " + recipe.key)}
|
|
7796
|
+
|
|
7797
|
+
`);
|
|
7798
|
+
} else if (tmpl) {
|
|
7799
|
+
stdout.write(`${c("bold", c("magenta", "template: " + tmpl.key))}
|
|
7800
|
+
|
|
7801
|
+
${tmpl.description}
|
|
7802
|
+
|
|
7803
|
+
Use it: ${c("cyan", "npx create-lacspace-app my-app --template " + tmpl.key)}
|
|
7804
|
+
|
|
7805
|
+
`);
|
|
7806
|
+
} else {
|
|
7807
|
+
stdout.write(c("yellow", `Unknown "${name}".
|
|
7808
|
+
`));
|
|
7809
|
+
runList();
|
|
7810
|
+
}
|
|
7811
|
+
}
|
|
7724
7812
|
async function main() {
|
|
7725
7813
|
const raw = argv.slice(2);
|
|
7726
7814
|
if (raw[0] === "add") {
|
|
7727
7815
|
runAdd(raw.slice(1));
|
|
7728
7816
|
return;
|
|
7729
7817
|
}
|
|
7818
|
+
if (raw[0] === "list" || raw[0] === "ls") {
|
|
7819
|
+
runList();
|
|
7820
|
+
return;
|
|
7821
|
+
}
|
|
7822
|
+
if (raw[0] === "explain" || raw[0] === "info") {
|
|
7823
|
+
runExplain(raw.slice(1));
|
|
7824
|
+
return;
|
|
7825
|
+
}
|
|
7730
7826
|
const args = parseArgs(raw);
|
|
7731
7827
|
if (args.help) {
|
|
7732
7828
|
stdout.write(HELP + "\n");
|
|
@@ -7745,10 +7841,32 @@ ${c("bold", c("magenta", "\u25C6 create-lacspace-app"))} ${c("dim", "\u2014 a go
|
|
|
7745
7841
|
let templateKey = args.template ?? recipe?.template;
|
|
7746
7842
|
let mode = args.mode ?? recipe?.mode ?? "static";
|
|
7747
7843
|
const featureKeys = [...recipe?.features ?? [], ...args.features];
|
|
7844
|
+
let usedRecipe = Boolean(recipe);
|
|
7748
7845
|
if (!args.yes && stdin.isTTY) {
|
|
7749
7846
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
7750
7847
|
try {
|
|
7751
7848
|
if (!name) name = (await rl.question(`${c("green", "?")} Project name ${c("dim", "(my-app)")}: `)).trim() || "my-app";
|
|
7849
|
+
if (!usedRecipe && !templateKey && featureKeys.length === 0) {
|
|
7850
|
+
stdout.write(`
|
|
7851
|
+
Start from a recipe? ${c("dim", "(a template + add-ons, ready to go)")}
|
|
7852
|
+
`);
|
|
7853
|
+
RECIPES.forEach((r, i) => stdout.write(` ${c("cyan", String(i + 1))}. ${c("bold", r.label)} ${c("dim", "\u2014 " + r.description)}
|
|
7854
|
+
`));
|
|
7855
|
+
stdout.write(` ${c("cyan", "0")}. ${c("dim", "No recipe \u2014 I'll choose a template myself")}
|
|
7856
|
+
`);
|
|
7857
|
+
const ans = (await rl.question(`
|
|
7858
|
+
${c("green", "?")} Recipe ${c("dim", "(0)")}: `)).trim() || "0";
|
|
7859
|
+
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : RECIPES.findIndex((r) => r.key === ans);
|
|
7860
|
+
const chosen = ans === "0" ? void 0 : RECIPES[idx];
|
|
7861
|
+
if (chosen) {
|
|
7862
|
+
usedRecipe = true;
|
|
7863
|
+
templateKey = chosen.template;
|
|
7864
|
+
mode = chosen.mode ?? mode;
|
|
7865
|
+
for (const k of chosen.features) if (!featureKeys.includes(k)) featureKeys.push(k);
|
|
7866
|
+
stdout.write(` ${c("green", "\u2714")} Recipe ${c("cyan", chosen.key)}
|
|
7867
|
+
`);
|
|
7868
|
+
}
|
|
7869
|
+
}
|
|
7752
7870
|
if (!templateKey) {
|
|
7753
7871
|
stdout.write(`
|
|
7754
7872
|
Choose a template:
|
|
@@ -7760,7 +7878,7 @@ ${c("green", "?")} Template ${c("dim", "(1)")}: `)).trim() || "1";
|
|
|
7760
7878
|
const idx = /^\d+$/.test(ans) ? parseInt(ans, 10) - 1 : TEMPLATES.findIndex((t) => t.key === ans);
|
|
7761
7879
|
templateKey = TEMPLATES[idx]?.key ?? "personal";
|
|
7762
7880
|
}
|
|
7763
|
-
if (args.mode === void 0 && !
|
|
7881
|
+
if (args.mode === void 0 && !usedRecipe) {
|
|
7764
7882
|
stdout.write(`
|
|
7765
7883
|
What kind of app?
|
|
7766
7884
|
`);
|
|
@@ -7819,6 +7937,19 @@ ${c("green", "?")} Add features? ${c("dim", "(comma-separated numbers, or Enter
|
|
|
7819
7937
|
`);
|
|
7820
7938
|
}
|
|
7821
7939
|
const files = buildFiles({ name: projectName, template, features, mode });
|
|
7940
|
+
if (args.dryRun) {
|
|
7941
|
+
const keys = Object.keys(files).sort();
|
|
7942
|
+
stdout.write(`
|
|
7943
|
+
${c("bold", "Dry run")} ${c("dim", `\u2014 ${keys.length} files (${mode}${features.length ? ", +" + features.map((f) => f.key).join(",") : ""}), nothing written`)}
|
|
7944
|
+
`);
|
|
7945
|
+
for (const rel of keys) stdout.write(` ${c("dim", "+ " + rel)}
|
|
7946
|
+
`);
|
|
7947
|
+
stdout.write(`
|
|
7948
|
+
${c("dim", "Remove --dry-run to create the project.")}
|
|
7949
|
+
|
|
7950
|
+
`);
|
|
7951
|
+
return;
|
|
7952
|
+
}
|
|
7822
7953
|
for (const [rel, content] of Object.entries(files)) {
|
|
7823
7954
|
const full = join(dir, rel);
|
|
7824
7955
|
mkdirSync(dirname(full), { recursive: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lacspace-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Scaffold a beautiful, production-ready Next.js app from a Lacspace template — portfolio, business, e-commerce, SaaS, blog, docs, dashboard, restaurant or marketplace — pre-wired with SEO, security headers, sitemap and robots. Use it as a CLI (like create-next-app, but you start gorgeous) or import it as a library to generate projects programmatically.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|