create-lacspace-app 1.14.0 → 1.15.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/BENCHMARKS.md ADDED
@@ -0,0 +1,52 @@
1
+ # Benchmarks
2
+
3
+ A fair, reproducible head-to-head against `create-next-app`. We benchmark **scaffold time only** (the CLI writing files to disk) — dependency installation is excluded from both, because it's identical `npm install` work that depends on your network, not the tool.
4
+
5
+ ## Method
6
+
7
+ - Both CLIs invoked **binary-direct** (`node .../dist/index.js`), not via `npx`/`npm create` (which add package-resolution overhead to both, unfairly and unequally).
8
+ - **Scaffold-only**: `--skip-install`/`--no-install` and git disabled on both.
9
+ - Median of **7 runs** on the same machine, back-to-back.
10
+ - `create-next-app` given the closest-matching flags (TypeScript, Tailwind, App Router, import alias).
11
+
12
+ ## Reproduce it yourself
13
+
14
+ ```bash
15
+ mkdir bench && cd bench
16
+ npm i create-next-app@latest create-lacspace-app@latest
17
+
18
+ CNA=node_modules/create-next-app/dist/index.js
19
+ CLA=node_modules/create-lacspace-app/dist/index.js
20
+
21
+ # create-lacspace-app — finished app, scaffold only
22
+ for i in 1 2 3 4 5 6 7; do rm -rf cla$i; \
23
+ /usr/bin/time -p node "$CLA" cla$i --template saas --no-install --no-git -y >/dev/null 2>&1; done
24
+
25
+ # create-next-app — blank app, scaffold only
26
+ for i in 1 2 3 4 5 6 7; do rm -rf cna$i; \
27
+ /usr/bin/time -p node "$CNA" cna$i --ts --tailwind --app --no-eslint --no-src-dir \
28
+ --skip-install --disable-git --no-turbopack --import-alias "@/*" >/dev/null 2>&1; done
29
+
30
+ # compare what you got
31
+ echo "lacspace files: $(find cla1 -type f | wc -l) pages: $(find cla1 -name page.tsx | wc -l)"
32
+ echo "next files: $(find cna1 -type f | wc -l) pages: $(find cna1 -name page.tsx | wc -l)"
33
+ ```
34
+
35
+ ## Results (create-next-app 16.3.4 vs create-lacspace-app 1.14.0)
36
+
37
+ | Metric | create-next-app | create-lacspace-app |
38
+ | --- | --- | --- |
39
+ | Scaffold time (median of 7) | **0.29s** | **0.15s** |
40
+ | Files written | 18 | 70 |
41
+ | `page.tsx` routes | 1 | 11 |
42
+ | Components | 0 | 37 (incl. a 26-component UI kit) |
43
+ | `sitemap.xml` / `robots.txt` / `/og` | ✗ | ✓ |
44
+ | `lib/site.ts` SEO config / JSON-LD | ✗ | ✓ |
45
+ | PWA manifest / styled 404 | ✗ | ✓ |
46
+ | Templates available | 1 | 8 |
47
+
48
+ ## Honest caveats
49
+
50
+ - **This is not a "10× faster framework" claim.** Both scaffolders finish in well under a second; the ~1.9× gap is real but small in absolute terms. `create-lacspace-app` is faster largely because it writes precomputed template strings with no interactive prompt round-trips.
51
+ - The **meaningful** difference is *output*: `create-next-app` hands you a single blank page and no SEO; `create-lacspace-app` hands you a running, multi-page, SEO-complete app you can deploy immediately.
52
+ - "Time to a deployable, SEO-ready site" — the metric that actually matters to a developer — is where the gap is large, because with `create-next-app` that work is still ahead of you.
package/README.md CHANGED
@@ -2,76 +2,120 @@
2
2
 
3
3
  # create-lacspace-app
4
4
 
5
- **Scaffold a beautiful, production-ready Next.js app in one command pick a template, start gorgeous.**
5
+ **`create-next-app` gives you a blank page. This gives you a finished, SEO-complete app in one command.**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/create-lacspace-app?color=%2316a34a&label=npm)](https://www.npmjs.com/package/create-lacspace-app)
8
+ [![install size](https://img.shields.io/badge/scaffold-~0.15s-16a34a)](#-benchmarks-honest-and-reproducible)
8
9
  [![license](https://img.shields.io/npm/l/create-lacspace-app?color=green)](https://github.com/lacspace/npm-packages/blob/main/LICENSE)
9
10
 
10
11
  </div>
11
12
 
12
- > `create-next-app` gives you a blank page. This gives you a **finished-looking app** — a polished template, styled with Tailwind, already wired with the Lacspace libraries (SEO metadata + JSON-LD, security headers, sitemap & robots). Choose your kind of site and you're running in seconds.
13
-
14
- ## Use it
15
-
16
13
  ```bash
17
14
  npm create lacspace-app@latest my-app
18
- # or
19
- npx create-lacspace-app my-app --template business
15
+ # or pick a template up front
16
+ npx create-lacspace-app my-app --template saas
20
17
  ```
21
18
 
22
- It asks for a project name and a template, scaffolds the project, installs dependencies and initialises git.
19
+ 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 polished, dark-themed, responsive site with **every page filled in**, an SEO stack wired end-to-end, and a **26-component UI kit** you can drop in anywhere.
23
20
 
24
- ## Templates
21
+ ---
25
22
 
26
- | Key | What you get |
27
- | --- | --- |
28
- | **personal** | A sleek personal / developer portfolio with a projects grid |
29
- | **business** | A professional company / agency site with services + CTA |
30
- | **ecommerce** | A modern storefront home with a featured product grid |
31
- | **saas** | A high-converting SaaS landing page with features + pricing |
32
- | **blog** | An editorial blog / magazine home with a featured post + grid |
33
- | **docs** | A documentation landing with quick-start + feature cards |
34
- | **dashboard** | An admin app shell with a sidebar, stat cards and activity |
35
- | **restaurant** | A warm restaurant / cafe site with a menu and reservations |
23
+ ## Benchmarks honest and reproducible
24
+
25
+ Same machine, both CLIs run **binary-direct, scaffold-only**, median of 7 runs:
26
+
27
+ | | create-next-app **16.3.4** | create-lacspace-app **1.14** |
28
+ | --- | --- | --- |
29
+ | **Scaffold time** | 0.29s | **0.15s** (~1.9× faster) |
30
+ | **Files produced** | 18 | **70** |
31
+ | **Pages** | 1 (blank) | **11** (finished, 5–6 sections each) |
32
+ | **Prebuilt components** | 0 | **37** (incl. a 26-component UI kit) |
33
+ | **Templates** | 1 | **8** |
34
+ | SEO metadata + JSON-LD | ✗ | ✓ |
35
+ | Dynamic OG images (`/og`) | ✗ | ✓ |
36
+ | `sitemap.xml` + `robots.txt` | ✗ | ✓ |
37
+ | Security headers (HSTS/CSP…) | ✗ | ✓ |
38
+ | PWA manifest + styled 404 | ✗ | ✓ |
39
+ | SEO regression CI gate | ✗ | ✓ |
40
+
41
+ > It's faster *and* you get a finished app instead of a blank one. Don't take our word for it — **[reproduce it in 60 seconds](./BENCHMARKS.md)**.
42
+
43
+ *(Numbers are scaffold-only. The honest headline isn't the milliseconds — both are sub-second — it's that one produces a blank page and the other produces a running, SEO-complete, multi-page app.)*
44
+
45
+ ---
46
+
47
+ ## 🎁 What you actually get
48
+
49
+ Every generated app arrives **done**, not started:
36
50
 
37
- Every template is a real Next.js 15 App Router + Tailwind v4 project dark, modern, responsive, with a gradient accent you can theme.
51
+ - 🧩 **8 templates**, each a complete site`personal`, `business`, `ecommerce`, `saas`, `blog`, `docs`, `dashboard`, `restaurant`.
52
+ - 📄 **Every page is filled** — home, about, pricing, services, features, FAQ, careers, work/case-studies, collections, integrations, changelog, gallery, reservations, and more. Each is **5–6 real sections** (hero → feature split with illustration → stat band → feature grid → showcase → CTA), personalised with your project name. No "coming soon" stubs.
53
+ - 🎨 **A 26-component UI kit** (`components/ui/`): `Section`, `Hero`, `FeatureCard`, `FeatureSplit`, `Bento`, `PricingTable`, `Testimonial`, `StatBand`, `Timeline`, `Steps`, `Tabs`, `Accordion`, `FAQ`, `Gallery`, `TeamGrid`, `LogoCloud`, `CTABand`, `Newsletter`, `AreaChart`, `Badge`, `Callout`, `Rating`, `Progress`, `Breadcrumbs`, `Avatar` … all theme-aware, dependency-free, server-first.
54
+ - 🔎 **SEO, done for you** — one [`@lacspace/seo`](https://www.npmjs.com/package/@lacspace/seo) `defineSite()` config drives `<title>`, canonical, Open Graph, Twitter, and JSON-LD across every route.
55
+ - ✨ **Dynamic OG images** at `/og` — a social-share card per page, no design tool.
56
+ - 🛡️ **Security headers** (HSTS, CSP, X-Frame-Options…) via [`@lacspace/headers`](https://www.npmjs.com/package/@lacspace/headers).
57
+ - 🗺️ **`sitemap.xml` + `robots.txt`** generated from your config.
58
+ - 🌗 **Dark / light / system theme** with a no-flash script, global header + footer, ⌘K command palette, and a mobile menu — all prewired.
59
+ - 🤖 **An SEO CI gate** (`.github/workflows/seo.yml`) that fails your build the moment SEO regresses.
38
60
 
39
- ## A complete, polished project not a blank page
61
+ Edit `lib/site.ts` (your site config) and `app/page.tsx` you're off.
40
62
 
41
- Every generated app arrives finished, with the boring-but-essential things done:
63
+ ---
64
+
65
+ ## 🧩 Grow your app anytime — `add`
66
+
67
+ Unlike every other `create-*` tool, `create-lacspace-app` doesn't stop after day one. Drop **prebuilt, themed sections** into an existing app whenever you need them:
68
+
69
+ ```bash
70
+ npx create-lacspace-app add pricing faq testimonials
71
+ ```
72
+
73
+ That writes ready-to-use sections into `components/sections/` (and installs the UI kit automatically if it's missing — so it works in **any** Next.js app, not just ones we scaffolded). Then:
42
74
 
43
- - 🎨 **Beautiful home page** — Tailwind v4, the Inter font, dark theme + a gradient accent
44
- - 🔎 **SEO** one [`@lacspace/seo`](https://www.npmjs.com/package/@lacspace/seo) `defineSite()` config drives your `<title>`, canonical, Open Graph, Twitter card and JSON-LD
45
- - **Dynamic OG images** every page auto-generates a social-share card at `/og` (no design tool)
46
- - 🛡️ **Security headers** — HSTS, CSP, X-Frame-Options and more via [`@lacspace/headers`](https://www.npmjs.com/package/@lacspace/headers)
47
- - 🗺️ **robots.txt + sitemap.xml** — generated from your config with [`@lacspace/robots`](https://www.npmjs.com/package/@lacspace/robots) & [`@lacspace/sitemap`](https://www.npmjs.com/package/@lacspace/sitemap)
48
- - 🧩 **A styled 404, a PWA manifest, and `.env.example`** the finishing touches most starters skip
49
- - 🎁 **A `WELCOME.md`** that walks you through everything and shows the bonus packages you can drop in
75
+ ```tsx
76
+ import { PricingSection } from "@/components/sections/pricing";
77
+ // …drop <PricingSection /> anywhere in your JSX.
78
+ ```
79
+
80
+ **Available sections:** `hero` · `features` · `pricing` · `faq` · `testimonials` · `team` · `stats` · `timeline` · `gallery` · `logos` · `cta` · `bento`. Run `add` with no arguments to list them.
81
+
82
+ ## Templates
83
+
84
+ | Key | What you get |
85
+ | --- | --- |
86
+ | **personal** | Developer/portfolio — projects, uses, work, about, blog |
87
+ | **business** | Agency/company — services, work (case studies), pricing, team |
88
+ | **ecommerce** | Storefront — shop, cart (working, persisted), collections |
89
+ | **saas** | Landing — features, pricing, integrations, changelog |
90
+ | **blog** | Real Markdown blog — posts, topics, newsletter |
91
+ | **docs** | Docs site — guides, API reference, changelog, search |
92
+ | **dashboard** | App shell — sidebar, stat cards, charts, settings, analytics |
93
+ | **restaurant** | Menu, reservations, gallery, private events |
50
94
 
51
- Edit `lib/site.ts` and `app/page.tsx` and you're off.
95
+ Every template is Next.js 15 App Router + React 19 + Tailwind v4 — dark, modern, responsive, with a themeable gradient accent.
52
96
 
53
97
  ## Options
54
98
 
55
99
  | Flag | Meaning |
56
100
  | --- | --- |
57
- | `-t, --template <key>` | `personal` \| `business` \| `ecommerce` \| `saas` |
58
- | `--pm <npm\|pnpm\|yarn\|bun>` | package manager (default npm) |
101
+ | `-t, --template <key>` | `personal` · `business` · `ecommerce` · `saas` · `blog` · `docs` · `dashboard` · `restaurant` |
102
+ | `--pm <npm\|pnpm\|yarn\|bun>` | package manager (default `npm`) |
59
103
  | `--no-install` | skip installing dependencies |
60
104
  | `--no-git` | skip git init |
61
105
  | `-y, --yes` | accept defaults (needs a project name) |
62
106
 
63
107
  ## Licensing
64
108
 
65
- Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; just keep the notice. The apps you generate are yours. See the **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
109
+ Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; the apps you generate are entirely yours.
66
110
 
67
111
  ---
68
112
 
69
113
  <div align="center">
70
114
 
71
- **Part of the Lacspace ecosystem — zero-dependency, isomorphic TypeScript packages.**
115
+ **Part of the Lacspace ecosystem — 60+ zero-dependency, isomorphic TypeScript packages.**
72
116
 
73
- [All packages ↗](https://lacspace.com/packages) · [npm org ↗](https://www.npmjs.com/org/lacspace) · [Licence Centre ↗](https://lacspace.com/licenses) · [GitHub ↗](https://github.com/lacspace/npm-packages)
117
+ [All packages ↗](https://lacspace.com/packages) · [Docs ↗](https://lacspace.com/docs) · [npm org ↗](https://www.npmjs.com/org/lacspace) · [GitHub ↗](https://github.com/lacspace/npm-packages)
74
118
 
75
119
  </div>
76
120
 
77
- <div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence · <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
121
+ <div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence</sub></div>
package/dist/index.js CHANGED
@@ -3462,6 +3462,7 @@ ${c("bold", "create-lacspace-app")} \u2014 scaffold a beautiful Next.js app, Lac
3462
3462
  ${c("bold", "Usage")}
3463
3463
  npm create lacspace-app@latest <name> [options]
3464
3464
  npx create-lacspace-app <name> --template <key>
3465
+ npx create-lacspace-app add <section...> ${c("dim", "# grow an existing app")}
3465
3466
 
3466
3467
  ${c("bold", "Templates")}
3467
3468
  ${TEMPLATES.map((t) => ` ${t.key.padEnd(10)} ${t.description}`).join("\n")}
@@ -3473,9 +3474,272 @@ ${c("bold", "Options")}
3473
3474
  --no-git Skip git init
3474
3475
  -y, --yes Accept defaults (needs <name>)
3475
3476
  -h, --help Show this help
3477
+
3478
+ ${c("bold", "Add sections to an existing app")}
3479
+ npx create-lacspace-app add pricing faq testimonials
3480
+ ${c("dim", "Drops prebuilt, themed sections into components/sections/ (and the UI kit if missing).")}
3476
3481
  `;
3482
+ var SECTIONS = {
3483
+ hero: `import Link from "next/link";
3484
+ import { Pill } from "@/components/ui";
3485
+
3486
+ export function HeroSection() {
3487
+ return (
3488
+ <section className="mx-auto max-w-4xl px-6 py-28 text-center">
3489
+ <Pill>New</Pill>
3490
+ <h1 className="mt-5 text-5xl font-bold sm:text-6xl">Build something <span className="gradient-text">people love</span></h1>
3491
+ <p className="mx-auto mt-5 max-w-xl text-lg text-muted">A confident headline and one sentence that sells the outcome \u2014 edit me.</p>
3492
+ <div className="mt-8 flex flex-wrap justify-center gap-3">
3493
+ <Link href="/contact" className="rounded-full gradient-bg px-7 py-3 font-semibold text-black transition hover:opacity-90">Get started</Link>
3494
+ <Link href="/about" className="rounded-full border border-hairline px-7 py-3 font-semibold transition hover:bg-surface">Learn more</Link>
3495
+ </div>
3496
+ </section>
3497
+ );
3498
+ }
3499
+ `,
3500
+ features: `import { Section, FeatureCard } from "@/components/ui";
3501
+
3502
+ const ITEMS = [
3503
+ { icon: "\u26A1", title: "Fast", desc: "Server-rendered and instant by default." },
3504
+ { icon: "\u{1F512}", title: "Secure", desc: "Sensible security headers out of the box." },
3505
+ { icon: "\u{1F3A8}", title: "Beautiful", desc: "A polished, themeable design system." },
3506
+ { icon: "\u{1F50E}", title: "SEO-ready", desc: "Metadata, JSON-LD and sitemaps wired." },
3507
+ { icon: "\u{1F4F1}", title: "Responsive", desc: "Looks great on every screen." },
3508
+ { icon: "\u{1F317}", title: "Dark mode", desc: "Light, dark and system \u2014 no flash." },
3509
+ ];
3510
+
3511
+ export function FeaturesSection() {
3512
+ return (
3513
+ <Section eyebrow="Features" title="Everything you need">
3514
+ <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
3515
+ {ITEMS.map((f) => <FeatureCard key={f.title} icon={f.icon} title={f.title} desc={f.desc} />)}
3516
+ </div>
3517
+ </Section>
3518
+ );
3519
+ }
3520
+ `,
3521
+ pricing: `import { Section, PricingTable } from "@/components/ui";
3522
+
3523
+ export function PricingSection() {
3524
+ return (
3525
+ <Section eyebrow="Pricing" title="Simple, transparent pricing">
3526
+ <PricingTable tiers={[
3527
+ { name: "Starter", price: "$0", period: "mo", features: ["1 project", "Community support", "Basic analytics"] },
3528
+ { name: "Pro", price: "$29", period: "mo", featured: true, features: ["Unlimited projects", "Priority support", "Advanced analytics", "Custom domain"] },
3529
+ { name: "Scale", price: "Custom", features: ["SSO & SAML", "Dedicated support", "SLA & audit logs"] },
3530
+ ]} />
3531
+ </Section>
3532
+ );
3533
+ }
3534
+ `,
3535
+ faq: `import { Section, FAQ } from "@/components/ui";
3536
+
3537
+ export function FaqSection() {
3538
+ return (
3539
+ <Section className="pt-0">
3540
+ <FAQ title="Frequently asked" items={[
3541
+ { q: "Is it production-ready?", a: "Yes \u2014 server-rendered, SEO-optimized and secure by default." },
3542
+ { q: "Can I customize it?", a: "Completely. Colours, fonts and layout are token-driven." },
3543
+ { q: "Does it support dark mode?", a: "Light, dark and system themes with a no-flash script." },
3544
+ { q: "How do I deploy?", a: "One click to any Node host or Vercel." },
3545
+ ]} />
3546
+ </Section>
3547
+ );
3548
+ }
3549
+ `,
3550
+ testimonials: `import { Section, Testimonial } from "@/components/ui";
3551
+
3552
+ const QUOTES = [
3553
+ { quote: "The smoothest launch we've ever had.", author: "Sam Rivera", role: "Product Lead" },
3554
+ { quote: "Fast, polished and easy to build on.", author: "Jordan Ellis", role: "Founder" },
3555
+ { quote: "The attention to detail shows everywhere.", author: "Priya Nair", role: "Design Director" },
3556
+ ];
3557
+
3558
+ export function TestimonialsSection() {
3559
+ return (
3560
+ <Section eyebrow="Loved by teams" title="Don't just take our word for it">
3561
+ <div className="grid gap-6 md:grid-cols-3">
3562
+ {QUOTES.map((t) => <Testimonial key={t.author} quote={t.quote} author={t.author} role={t.role} />)}
3563
+ </div>
3564
+ </Section>
3565
+ );
3566
+ }
3567
+ `,
3568
+ team: `import { Section, TeamGrid } from "@/components/ui";
3569
+
3570
+ export function TeamSection() {
3571
+ return (
3572
+ <Section eyebrow="The team" title="The people behind it">
3573
+ <TeamGrid members={[
3574
+ { name: "Alex Morgan", role: "Founder & CEO", bio: "Sets the vision and keeps us honest." },
3575
+ { name: "Riya Sharma", role: "Head of Design", bio: "Makes every pixel earn its place." },
3576
+ { name: "Chris Doyle", role: "Lead Engineer", bio: "Ships fast without breaking things." },
3577
+ ]} />
3578
+ </Section>
3579
+ );
3580
+ }
3581
+ `,
3582
+ stats: `import { Section, StatBand } from "@/components/ui";
3583
+
3584
+ export function StatsSection() {
3585
+ return (
3586
+ <Section eyebrow="By the numbers" title="Built to perform">
3587
+ <StatBand stats={[
3588
+ { value: "10k+", label: "Monthly visitors" },
3589
+ { value: "99.9%", label: "Uptime" },
3590
+ { value: "4.9\u2605", label: "Average rating" },
3591
+ { value: "<1s", label: "Load time" },
3592
+ ]} />
3593
+ </Section>
3594
+ );
3595
+ }
3596
+ `,
3597
+ timeline: `import { Section, Timeline } from "@/components/ui";
3598
+
3599
+ export function TimelineSection() {
3600
+ return (
3601
+ <Section eyebrow="Our story" title="How we got here">
3602
+ <div className="mx-auto max-w-2xl">
3603
+ <Timeline items={[
3604
+ { date: "Then", title: "The idea", desc: "It started with a simple frustration." },
3605
+ { date: "Next", title: "First release", desc: "We shipped, and people showed up." },
3606
+ { date: "Now", title: "Growing fast", desc: "Thousands of teams, and counting." },
3607
+ ]} />
3608
+ </div>
3609
+ </Section>
3610
+ );
3611
+ }
3612
+ `,
3613
+ gallery: `import { Section, Gallery } from "@/components/ui";
3614
+
3615
+ export function GallerySection() {
3616
+ return (
3617
+ <Section eyebrow="Gallery" title="A look inside">
3618
+ <Gallery items={[
3619
+ { emoji: "\u{1F5BC}\uFE0F", label: "One" }, { emoji: "\u{1F306}", label: "Two" }, { emoji: "\u{1F3A8}", label: "Three" },
3620
+ { emoji: "\u{1F4F8}", label: "Four" }, { emoji: "\u2728", label: "Five" }, { emoji: "\u{1F31F}", label: "Six" },
3621
+ ]} />
3622
+ </Section>
3623
+ );
3624
+ }
3625
+ `,
3626
+ logos: `import { Section, LogoCloud } from "@/components/ui";
3627
+
3628
+ export function LogosSection() {
3629
+ return (
3630
+ <Section className="pt-0">
3631
+ <LogoCloud label="Trusted by teams at" names={["Northwind", "Globex", "Initech", "Umbrella", "Soylent", "Hooli"]} />
3632
+ </Section>
3633
+ );
3634
+ }
3635
+ `,
3636
+ cta: `import { CTABand } from "@/components/ui";
3637
+
3638
+ export function CtaSection() {
3639
+ return <CTABand title="Ready to get started?" subtitle="Join thousands of teams building with us." ctaLabel="Get started" ctaHref="/contact" />;
3640
+ }
3641
+ `,
3642
+ bento: `import { Section, Bento } from "@/components/ui";
3643
+
3644
+ export function BentoSection() {
3645
+ return (
3646
+ <Section eyebrow="One platform" title="Everything, together">
3647
+ <Bento items={[
3648
+ { icon: "\u26A1", title: "Blazing fast", desc: "Instant page loads, everywhere.", className: "sm:col-span-2" },
3649
+ { icon: "\u{1F512}", title: "Secure", desc: "Locked down by default." },
3650
+ { icon: "\u{1F3A8}", title: "Themeable", desc: "Your brand, one token away." },
3651
+ { icon: "\u{1F4CA}", title: "Insightful", desc: "Know what's working.", className: "sm:col-span-2" },
3652
+ ]} />
3653
+ </Section>
3654
+ );
3655
+ }
3656
+ `
3657
+ };
3658
+ function ensureUiKit(root) {
3659
+ const added = [];
3660
+ for (const [rel, content] of Object.entries(uiKitFiles())) {
3661
+ const full = join(root, rel);
3662
+ if (!existsSync(full)) {
3663
+ mkdirSync(dirname(full), { recursive: true });
3664
+ writeFileSync(full, content);
3665
+ added.push(rel);
3666
+ }
3667
+ }
3668
+ return added;
3669
+ }
3670
+ var pascal = (s) => s.split(/[-_]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
3671
+ function runAdd(rawNames) {
3672
+ stdout.write(`
3673
+ ${c("bold", c("magenta", "\u25C6 create-lacspace-app add"))} ${c("dim", "\u2014 drop prebuilt sections into your app")}
3674
+
3675
+ `);
3676
+ const root = cwd();
3677
+ if (!existsSync(join(root, "package.json"))) {
3678
+ stdout.write(c("red", "\u2717 No package.json here. Run this inside your Next.js project.\n\n"));
3679
+ exit(1);
3680
+ return;
3681
+ }
3682
+ const wanted = rawNames.filter((n) => !n.startsWith("-")).map((n) => n.toLowerCase());
3683
+ if (wanted.length === 0 || rawNames.includes("--help") || rawNames.includes("-h")) {
3684
+ stdout.write(`Usage: ${c("cyan", "npx create-lacspace-app add <section...>")}
3685
+
3686
+ ${c("bold", "Available sections")}
3687
+ `);
3688
+ stdout.write(" " + Object.keys(SECTIONS).map((k) => c("cyan", k)).join(" ") + "\n");
3689
+ stdout.write(`
3690
+ Example: ${c("cyan", "npx create-lacspace-app add pricing faq testimonials")}
3691
+
3692
+ `);
3693
+ return;
3694
+ }
3695
+ const unknown = wanted.filter((n) => !(n in SECTIONS));
3696
+ if (unknown.length) stdout.write(c("yellow", `! Unknown: ${unknown.join(", ")} ${c("dim", "(run with no args to list)")}
3697
+ `));
3698
+ const toAdd = wanted.filter((n) => n in SECTIONS);
3699
+ if (toAdd.length === 0) {
3700
+ exit(1);
3701
+ return;
3702
+ }
3703
+ const kitAdded = ensureUiKit(root);
3704
+ if (kitAdded.length) stdout.write(` ${c("green", "\u2714")} Added the UI kit ${c("dim", "(" + kitAdded.length + " components)")}
3705
+ `);
3706
+ const created = [];
3707
+ for (const name of toAdd) {
3708
+ const rel = `components/sections/${name}.tsx`;
3709
+ const full = join(root, rel);
3710
+ if (existsSync(full)) {
3711
+ stdout.write(c("yellow", ` ~ ${rel} exists \u2014 skipped
3712
+ `));
3713
+ continue;
3714
+ }
3715
+ mkdirSync(dirname(full), { recursive: true });
3716
+ writeFileSync(full, SECTIONS[name]);
3717
+ created.push(name);
3718
+ stdout.write(` ${c("green", "\u2714")} ${c("dim", "+ " + rel)}
3719
+ `);
3720
+ }
3721
+ if (created.length) {
3722
+ stdout.write(`
3723
+ ${c("bold", "Use them")} \u2014 import into any page:
3724
+ `);
3725
+ for (const n of created) stdout.write(` ${c("cyan", `import { ${pascal(n)}Section } from "@/components/sections/${n}";`)}
3726
+ `);
3727
+ stdout.write(`
3728
+ Then drop ${c("cyan", "<" + pascal(created[0]) + "Section />")} into your JSX.
3729
+ `);
3730
+ stdout.write(`
3731
+ ${c("dim", "Sections use the @lacspace UI kit + design tokens \u2014 best inside a create-lacspace-app project.")}
3732
+
3733
+ `);
3734
+ }
3735
+ }
3477
3736
  async function main() {
3478
- const args = parseArgs(argv.slice(2));
3737
+ const raw = argv.slice(2);
3738
+ if (raw[0] === "add") {
3739
+ runAdd(raw.slice(1));
3740
+ return;
3741
+ }
3742
+ const args = parseArgs(raw);
3479
3743
  if (args.help) {
3480
3744
  stdout.write(HELP + "\n");
3481
3745
  return;
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "create-lacspace-app",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Scaffold a beautiful, production-ready Next.js app from a Lacspace template — portfolio, business, e-commerce, SaaS, blog, docs, dashboard or restaurant — pre-wired with SEO, security headers, sitemap and robots. Like create-next-app, but you start gorgeous.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-lacspace-app": "dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "BENCHMARKS.md"
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "tsup",