create-lacspace-app 1.13.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BENCHMARKS.md +52 -0
- package/README.md +63 -36
- package/dist/index.js +192 -7
- package/package.json +3 -2
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,103 @@
|
|
|
2
2
|
|
|
3
3
|
# create-lacspace-app
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**`create-next-app` gives you a blank page. This gives you a finished, SEO-complete app — in one command.**
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/create-lacspace-app)
|
|
8
|
+
[](#-benchmarks-honest-and-reproducible)
|
|
8
9
|
[](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
|
|
15
|
+
# or pick a template up front
|
|
16
|
+
npx create-lacspace-app my-app --template saas
|
|
20
17
|
```
|
|
21
18
|
|
|
22
|
-
It
|
|
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
|
-
|
|
21
|
+
---
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| **
|
|
31
|
-
|
|
|
32
|
-
| **
|
|
33
|
-
| **
|
|
34
|
-
| **
|
|
35
|
-
| **
|
|
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 | ✗ | ✓ |
|
|
36
40
|
|
|
37
|
-
|
|
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)**.
|
|
38
42
|
|
|
39
|
-
|
|
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
|
+
---
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
## 🎁 What you actually get
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
49
|
+
Every generated app arrives **done**, not started:
|
|
50
|
+
|
|
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.
|
|
60
|
+
|
|
61
|
+
Edit `lib/site.ts` (your site config) and `app/page.tsx` — you're off.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Templates
|
|
66
|
+
|
|
67
|
+
| Key | What you get |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| **personal** | Developer/portfolio — projects, uses, work, about, blog |
|
|
70
|
+
| **business** | Agency/company — services, work (case studies), pricing, team |
|
|
71
|
+
| **ecommerce** | Storefront — shop, cart (working, persisted), collections |
|
|
72
|
+
| **saas** | Landing — features, pricing, integrations, changelog |
|
|
73
|
+
| **blog** | Real Markdown blog — posts, topics, newsletter |
|
|
74
|
+
| **docs** | Docs site — guides, API reference, changelog, search |
|
|
75
|
+
| **dashboard** | App shell — sidebar, stat cards, charts, settings, analytics |
|
|
76
|
+
| **restaurant** | Menu, reservations, gallery, private events |
|
|
50
77
|
|
|
51
|
-
|
|
78
|
+
Every template is Next.js 15 App Router + React 19 + Tailwind v4 — dark, modern, responsive, with a themeable gradient accent.
|
|
52
79
|
|
|
53
80
|
## Options
|
|
54
81
|
|
|
55
82
|
| Flag | Meaning |
|
|
56
83
|
| --- | --- |
|
|
57
|
-
| `-t, --template <key>` | `personal`
|
|
58
|
-
| `--pm <npm\|pnpm\|yarn\|bun>` | package manager (default npm) |
|
|
84
|
+
| `-t, --template <key>` | `personal` · `business` · `ecommerce` · `saas` · `blog` · `docs` · `dashboard` · `restaurant` |
|
|
85
|
+
| `--pm <npm\|pnpm\|yarn\|bun>` | package manager (default `npm`) |
|
|
59
86
|
| `--no-install` | skip installing dependencies |
|
|
60
87
|
| `--no-git` | skip git init |
|
|
61
88
|
| `-y, --yes` | accept defaults (needs a project name) |
|
|
62
89
|
|
|
63
90
|
## Licensing
|
|
64
91
|
|
|
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;
|
|
92
|
+
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
93
|
|
|
67
94
|
---
|
|
68
95
|
|
|
69
96
|
<div align="center">
|
|
70
97
|
|
|
71
|
-
**Part of the Lacspace ecosystem — zero-dependency, isomorphic TypeScript packages.**
|
|
98
|
+
**Part of the Lacspace ecosystem — 60+ zero-dependency, isomorphic TypeScript packages.**
|
|
72
99
|
|
|
73
|
-
[All packages ↗](https://lacspace.com/packages) · [
|
|
100
|
+
[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
101
|
|
|
75
102
|
</div>
|
|
76
103
|
|
|
77
|
-
<div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence
|
|
104
|
+
<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
|
@@ -803,6 +803,183 @@ var usesPage = (ctx) => pageFile({
|
|
|
803
803
|
</div>
|
|
804
804
|
</Section>`
|
|
805
805
|
});
|
|
806
|
+
var richPage = (ctx, cfg) => {
|
|
807
|
+
const r = richContent(ctx);
|
|
808
|
+
const showcase = cfg.extra ?? `<Section eyebrow="Loved by teams" title="Don't just take our word for it" className="pt-0">
|
|
809
|
+
<div className="grid gap-6 md:grid-cols-3">
|
|
810
|
+
${r.testimonials.map((t) => `<Testimonial quote={${JSON.stringify(t.quote)}} author={${JSON.stringify(t.author)}} role={${JSON.stringify(t.role ?? "")}} />`).join("\n ")}
|
|
811
|
+
</div>
|
|
812
|
+
</Section>`;
|
|
813
|
+
return pageFile({
|
|
814
|
+
title: cfg.title,
|
|
815
|
+
path: cfg.path,
|
|
816
|
+
description: cfg.description,
|
|
817
|
+
cta: cfg.cta,
|
|
818
|
+
body: ` <section className="mx-auto max-w-3xl px-6 py-24 text-center">
|
|
819
|
+
<Pill>${cfg.eyebrow}</Pill>
|
|
820
|
+
<h1 className="mt-4 text-4xl font-bold sm:text-5xl">${cfg.heading}</h1>
|
|
821
|
+
<p className="mx-auto mt-4 max-w-xl text-lg text-muted">${cfg.lead}</p>
|
|
822
|
+
</section>
|
|
823
|
+
<Section className="pt-0">
|
|
824
|
+
<FeatureSplit eyebrow=${JSON.stringify(cfg.split.eyebrow ?? "")} title=${JSON.stringify(cfg.split.title)} desc=${JSON.stringify(cfg.split.desc)} bullets={${JSON.stringify(cfg.split.bullets)}} media={<div className="grid h-52 place-items-center text-7xl">${cfg.split.emoji}</div>} />
|
|
825
|
+
</Section>
|
|
826
|
+
<Section className="pt-0"><StatBand stats={${JSON.stringify(r.stats)}} /></Section>
|
|
827
|
+
<Section eyebrow="Highlights" title="What you get" className="pt-0">
|
|
828
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
829
|
+
{${JSON.stringify(cfg.features)}.map((f) => <FeatureCard key={f.title} icon={f.icon} title={f.title} desc={f.desc} />)}
|
|
830
|
+
</div>
|
|
831
|
+
</Section>
|
|
832
|
+
${showcase}`
|
|
833
|
+
});
|
|
834
|
+
};
|
|
835
|
+
var careersPage = (ctx) => {
|
|
836
|
+
const n = ctx.template.siteName;
|
|
837
|
+
return richPage(ctx, {
|
|
838
|
+
title: "Careers",
|
|
839
|
+
path: "/careers",
|
|
840
|
+
eyebrow: "Careers",
|
|
841
|
+
description: `Join the team building ${n}.`,
|
|
842
|
+
heading: `Join the <span className="gradient-text">team</span>`,
|
|
843
|
+
lead: `We're a small team doing our best work. If that sounds like you, say hello.`,
|
|
844
|
+
split: { eyebrow: "Why us", title: "A place to do your best work", desc: "Small team, big ownership, and the support to grow.", bullets: ["Remote-first and async-friendly", "Real ownership from day one", "Learning budget and great gear"], emoji: "\u{1F680}" },
|
|
845
|
+
features: [
|
|
846
|
+
{ icon: "\u{1F30D}", title: "Remote-first", desc: "Work from anywhere, on your schedule." },
|
|
847
|
+
{ icon: "\u{1F4C8}", title: "Grow fast", desc: "Mentorship and a budget to level up." },
|
|
848
|
+
{ icon: "\u{1F91D}", title: "Real ownership", desc: "Ship things that matter, end to end." },
|
|
849
|
+
{ icon: "\u{1F3D6}\uFE0F", title: "Time to recharge", desc: "Generous, actually-used time off." },
|
|
850
|
+
{ icon: "\u{1F499}", title: "Great people", desc: "Kind, sharp teammates who have your back." },
|
|
851
|
+
{ icon: "\u{1F4B8}", title: "Fair pay", desc: "Transparent, competitive compensation." }
|
|
852
|
+
],
|
|
853
|
+
extra: `<Section eyebrow="Open roles" title="We're hiring" className="pt-0">
|
|
854
|
+
<div className="mx-auto grid max-w-3xl gap-4">
|
|
855
|
+
{[
|
|
856
|
+
{ role: "Senior Frontend Engineer", team: "Engineering", type: "Full-time \xB7 Remote" },
|
|
857
|
+
{ role: "Product Designer", team: "Design", type: "Full-time \xB7 Remote" },
|
|
858
|
+
{ role: "Developer Advocate", team: "Growth", type: "Full-time \xB7 Remote" },
|
|
859
|
+
].map((j) => (
|
|
860
|
+
<div key={j.role} className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-hairline bg-surface p-5">
|
|
861
|
+
<div><h3 className="font-semibold">{j.role}</h3><p className="text-sm text-muted">{j.team}</p></div>
|
|
862
|
+
<div className="flex items-center gap-3"><Badge>{j.type}</Badge><a href="/contact" className="rounded-full gradient-bg px-4 py-2 text-sm font-semibold text-black">Apply</a></div>
|
|
863
|
+
</div>
|
|
864
|
+
))}
|
|
865
|
+
</div>
|
|
866
|
+
</Section>`,
|
|
867
|
+
cta: { title: "Don't see your role?", subtitle: "We're always keen to meet great people.", label: "Send an intro", href: "/contact" }
|
|
868
|
+
});
|
|
869
|
+
};
|
|
870
|
+
var collectionsPage = (ctx) => richPage(ctx, {
|
|
871
|
+
title: "Collections",
|
|
872
|
+
path: "/collections",
|
|
873
|
+
eyebrow: "Collections",
|
|
874
|
+
description: `Shop ${ctx.template.siteName} by collection.`,
|
|
875
|
+
heading: `Shop by <span className="gradient-text">collection</span>`,
|
|
876
|
+
lead: "Curated edits for every season and style.",
|
|
877
|
+
split: { eyebrow: "Featured", title: "This season's edit", desc: "Hand-picked pieces our team is loving right now.", bullets: ["Free shipping over $50", "30-day easy returns", "Ethically sourced"], emoji: "\u{1F6CD}\uFE0F" },
|
|
878
|
+
features: [
|
|
879
|
+
{ icon: "\u{1F9E5}", title: "New arrivals", desc: "Fresh drops, added weekly." },
|
|
880
|
+
{ icon: "\u{1F525}", title: "Best sellers", desc: "The pieces everyone's buying." },
|
|
881
|
+
{ icon: "\u{1F331}", title: "Sustainable", desc: "Made to last, kind to the planet." },
|
|
882
|
+
{ icon: "\u{1F381}", title: "Gifting", desc: "Thoughtful picks for everyone." },
|
|
883
|
+
{ icon: "\u{1F48E}", title: "Premium", desc: "Elevated essentials worth the splurge." },
|
|
884
|
+
{ icon: "\u{1F3F7}\uFE0F", title: "Sale", desc: "Up to 40% off, while stocks last." }
|
|
885
|
+
],
|
|
886
|
+
extra: `<Section eyebrow="Browse" title="Explore the collections" className="pt-0">
|
|
887
|
+
<Gallery items={[
|
|
888
|
+
{ emoji: "\u{1F9E5}", label: "Outerwear" }, { emoji: "\u{1F45F}", label: "Footwear" }, { emoji: "\u{1F45C}", label: "Bags" },
|
|
889
|
+
{ emoji: "\u231A", label: "Accessories" }, { emoji: "\u{1F576}\uFE0F", label: "Eyewear" }, { emoji: "\u{1F9E2}", label: "Headwear" },
|
|
890
|
+
]} />
|
|
891
|
+
</Section>`,
|
|
892
|
+
cta: { title: "Ready to shop?", subtitle: "Your next favourite thing is waiting.", label: "Browse the shop", href: "/shop" }
|
|
893
|
+
});
|
|
894
|
+
var businessWorkPage = (ctx) => richPage(ctx, {
|
|
895
|
+
title: "Work",
|
|
896
|
+
path: "/work",
|
|
897
|
+
eyebrow: "Case studies",
|
|
898
|
+
description: `Selected work from ${ctx.template.siteName}.`,
|
|
899
|
+
heading: `Work that <span className="gradient-text">performs</span>`,
|
|
900
|
+
lead: "A few recent projects and the outcomes they drove.",
|
|
901
|
+
split: { eyebrow: "How we work", title: "Outcomes, not just output", desc: "We measure success by the numbers that move your business.", bullets: ["Discovery-led, evidence-based", "Ship weekly, learn faster", "Own the result together"], emoji: "\u{1F4CA}" },
|
|
902
|
+
features: [
|
|
903
|
+
{ icon: "\u{1F6D2}", title: "Northwind Commerce", desc: "+38% conversion after a full storefront rebuild." },
|
|
904
|
+
{ icon: "\u{1F3E6}", title: "Globex Fintech", desc: "Onboarding time cut from 9 minutes to under 2." },
|
|
905
|
+
{ icon: "\u{1F393}", title: "Initech Learning", desc: "2.4\xD7 course completions with a redesigned LMS." },
|
|
906
|
+
{ icon: "\u{1F69A}", title: "Umbrella Logistics", desc: "Real-time tracking that cut support tickets 45%." },
|
|
907
|
+
{ icon: "\u{1F3E5}", title: "Soylent Health", desc: "HIPAA-ready portal shipped in eight weeks." },
|
|
908
|
+
{ icon: "\u{1F4F1}", title: "Hooli Mobile", desc: "A 4.9\u2605 app rebuilt from the ground up." }
|
|
909
|
+
],
|
|
910
|
+
cta: { title: "Have a project in mind?", subtitle: "Tell us the outcome you're after \u2014 we'll map the path.", label: "Start a project", href: "/contact" }
|
|
911
|
+
});
|
|
912
|
+
var apiRefPage = (ctx) => richPage(ctx, {
|
|
913
|
+
title: "API Reference",
|
|
914
|
+
path: "/api-reference",
|
|
915
|
+
eyebrow: "API",
|
|
916
|
+
description: `The ${ctx.template.siteName} REST API reference.`,
|
|
917
|
+
heading: `The <span className="gradient-text">API</span> reference`,
|
|
918
|
+
lead: "A predictable, typed REST API with sensible defaults.",
|
|
919
|
+
split: { eyebrow: "Basics", title: "REST, done right", desc: "JSON everywhere, cursor pagination, and clear error codes.", bullets: ["Bearer-token auth", "Idempotent writes", "Webhooks for every event"], emoji: "\u{1F50C}" },
|
|
920
|
+
features: [
|
|
921
|
+
{ icon: "\u{1F511}", title: "POST /auth/token", desc: "Exchange credentials for an access token." },
|
|
922
|
+
{ icon: "\u{1F464}", title: "GET /users/:id", desc: "Fetch a single user by id." },
|
|
923
|
+
{ icon: "\u{1F4E6}", title: "GET /resources", desc: "List resources with cursor pagination." },
|
|
924
|
+
{ icon: "\u270F\uFE0F", title: "PATCH /resources/:id", desc: "Partially update a resource." },
|
|
925
|
+
{ icon: "\u{1F5D1}\uFE0F", title: "DELETE /resources/:id", desc: "Remove a resource (idempotent)." },
|
|
926
|
+
{ icon: "\u{1FA9D}", title: "POST /webhooks", desc: "Subscribe to real-time events." }
|
|
927
|
+
],
|
|
928
|
+
extra: `<Section eyebrow="Examples" title="Common requests" className="pt-0">
|
|
929
|
+
<div className="mx-auto max-w-3xl"><Accordion items={[
|
|
930
|
+
{ q: "Authenticate", a: "curl -X POST /auth/token -d '{ \\"key\\": \\"...\\" }' \u2014 returns a bearer token valid for 24h." },
|
|
931
|
+
{ q: "List with pagination", a: "GET /resources?limit=20&cursor=... \u2014 responses include a next_cursor field." },
|
|
932
|
+
{ q: "Handle errors", a: "Every error returns a JSON body with a code and message, plus the right HTTP status." },
|
|
933
|
+
]} /></div>
|
|
934
|
+
</Section>`,
|
|
935
|
+
cta: { title: "Need a hand?", subtitle: "Our team is happy to help you integrate.", label: "Talk to us", href: "/contact" }
|
|
936
|
+
});
|
|
937
|
+
var reservationsPage = (ctx) => {
|
|
938
|
+
const n = ctx.template.siteName;
|
|
939
|
+
return richPage(ctx, {
|
|
940
|
+
title: "Reservations",
|
|
941
|
+
path: "/reservations",
|
|
942
|
+
eyebrow: "Reservations",
|
|
943
|
+
description: `Book your table at ${n}.`,
|
|
944
|
+
heading: `Reserve your <span className="gradient-text">table</span>`,
|
|
945
|
+
lead: `We can't wait to host you at ${n}. Book ahead \u2014 we fill up fast.`,
|
|
946
|
+
split: { eyebrow: "Good to know", title: "Everything for a perfect evening", desc: "Walk-ins welcome, but reservations are recommended, especially on weekends.", bullets: ["Parties up to 12 online", "Private dining available", "Dietary needs? Just ask"], emoji: "\u{1F37D}\uFE0F" },
|
|
947
|
+
features: [
|
|
948
|
+
{ icon: "\u{1F570}\uFE0F", title: "Lunch", desc: "Tue\u2013Fri \xB7 12:00\u201315:00" },
|
|
949
|
+
{ icon: "\u{1F319}", title: "Dinner", desc: "Tue\u2013Sun \xB7 18:00\u201323:00" },
|
|
950
|
+
{ icon: "\u{1F942}", title: "Weekend brunch", desc: "Sat\u2013Sun \xB7 10:00\u201314:00" },
|
|
951
|
+
{ icon: "\u{1F389}", title: "Private events", desc: "Book the whole room." },
|
|
952
|
+
{ icon: "\u{1F697}", title: "Parking", desc: "Valet available on evenings." },
|
|
953
|
+
{ icon: "\u267F", title: "Accessible", desc: "Step-free access throughout." }
|
|
954
|
+
],
|
|
955
|
+
extra: `<Section className="pt-0">
|
|
956
|
+
<div className="mx-auto max-w-2xl rounded-3xl border border-hairline bg-surface p-8 text-center">
|
|
957
|
+
<h2 className="text-2xl font-bold">Book by phone or online</h2>
|
|
958
|
+
<p className="mt-3 text-muted">Call us on <span className="font-semibold text-fg">+1 (555) 012-3456</span> or reserve online in seconds.</p>
|
|
959
|
+
<a href="/contact" className="mt-6 inline-block rounded-full gradient-bg px-8 py-3 font-semibold text-black">Reserve a table</a>
|
|
960
|
+
</div>
|
|
961
|
+
</Section>`,
|
|
962
|
+
cta: { title: "Planning something special?", subtitle: "Ask us about private dining and set menus.", label: "Enquire now", href: "/contact" }
|
|
963
|
+
});
|
|
964
|
+
};
|
|
965
|
+
var eventsPage = (ctx) => richPage(ctx, {
|
|
966
|
+
title: "Private events",
|
|
967
|
+
path: "/events",
|
|
968
|
+
eyebrow: "Private events",
|
|
969
|
+
description: `Host your event at ${ctx.template.siteName}.`,
|
|
970
|
+
heading: `Your event, <span className="gradient-text">elevated</span>`,
|
|
971
|
+
lead: "From intimate dinners to full buy-outs \u2014 we'll make it unforgettable.",
|
|
972
|
+
split: { eyebrow: "Occasions", title: "Made for the moments that matter", desc: "Birthdays, launches, weddings and everything in between.", bullets: ["Bespoke set menus", "Dedicated event host", "AV and styling on request"], emoji: "\u{1F389}" },
|
|
973
|
+
features: [
|
|
974
|
+
{ icon: "\u{1F382}", title: "Celebrations", desc: "Birthdays and anniversaries, done right." },
|
|
975
|
+
{ icon: "\u{1F4BC}", title: "Corporate", desc: "Launches, offsites and client dinners." },
|
|
976
|
+
{ icon: "\u{1F48D}", title: "Weddings", desc: "Rehearsal dinners and receptions." },
|
|
977
|
+
{ icon: "\u{1F377}", title: "Tastings", desc: "Guided wine and menu pairings." },
|
|
978
|
+
{ icon: "\u{1F465}", title: "Buy-outs", desc: "The whole space, just for you." },
|
|
979
|
+
{ icon: "\u{1F3B6}", title: "Live music", desc: "We'll arrange the perfect soundtrack." }
|
|
980
|
+
],
|
|
981
|
+
cta: { title: "Let's plan your event", subtitle: "Tell us the date and the vibe \u2014 we'll handle the rest.", label: "Start planning", href: "/contact" }
|
|
982
|
+
});
|
|
806
983
|
var themeToggle = () => `"use client";
|
|
807
984
|
|
|
808
985
|
import { useTheme } from "@lacspace/theme";
|
|
@@ -1631,7 +1808,7 @@ function pagesFor(ctx) {
|
|
|
1631
1808
|
const common = [
|
|
1632
1809
|
{ path: "/about", label: "About", nav: true, group: "Company", real: true },
|
|
1633
1810
|
{ path: "/contact", label: "Contact", nav: true, group: "Company", real: true },
|
|
1634
|
-
{ path: "/careers", label: "Careers", group: "Company" },
|
|
1811
|
+
{ path: "/careers", label: "Careers", group: "Company", real: true },
|
|
1635
1812
|
{ path: "/faq", label: "FAQ", group: "Resources", real: true },
|
|
1636
1813
|
{ path: "/privacy", label: "Privacy", group: "Resources" },
|
|
1637
1814
|
{ path: "/terms", label: "Terms", group: "Resources" }
|
|
@@ -1644,12 +1821,12 @@ function pagesFor(ctx) {
|
|
|
1644
1821
|
],
|
|
1645
1822
|
business: [
|
|
1646
1823
|
{ path: "/services", label: "Services", nav: true, group: "Product", real: true },
|
|
1647
|
-
{ path: "/work", label: "Work", nav: true, group: "Product" },
|
|
1824
|
+
{ path: "/work", label: "Work", nav: true, group: "Product", real: true },
|
|
1648
1825
|
{ path: "/pricing", label: "Pricing", nav: true, group: "Product", real: true }
|
|
1649
1826
|
],
|
|
1650
1827
|
ecommerce: [
|
|
1651
1828
|
{ path: "/shop", label: "Shop", nav: true, group: "Product", real: true },
|
|
1652
|
-
{ path: "/collections", label: "Collections", nav: true, group: "Product" },
|
|
1829
|
+
{ path: "/collections", label: "Collections", nav: true, group: "Product", real: true },
|
|
1653
1830
|
{ path: "/cart", label: "Cart", group: "Product", real: true },
|
|
1654
1831
|
{ path: "/shipping", label: "Shipping", group: "Resources" },
|
|
1655
1832
|
{ path: "/returns", label: "Returns", group: "Resources" }
|
|
@@ -1668,14 +1845,14 @@ function pagesFor(ctx) {
|
|
|
1668
1845
|
docs: [
|
|
1669
1846
|
{ path: "/docs", label: "Docs", nav: true, group: "Product", real: true },
|
|
1670
1847
|
{ path: "/guides", label: "Guides", nav: true, group: "Product", real: true },
|
|
1671
|
-
{ path: "/api-reference", label: "API", nav: true, group: "Product" },
|
|
1848
|
+
{ path: "/api-reference", label: "API", nav: true, group: "Product", real: true },
|
|
1672
1849
|
{ path: "/changelog", label: "Changelog", group: "Resources", real: true }
|
|
1673
1850
|
],
|
|
1674
1851
|
restaurant: [
|
|
1675
1852
|
{ path: "/menu", label: "Menu", nav: true, group: "Product", real: true },
|
|
1676
|
-
{ path: "/reservations", label: "Reservations", nav: true, group: "Product" },
|
|
1853
|
+
{ path: "/reservations", label: "Reservations", nav: true, group: "Product", real: true },
|
|
1677
1854
|
{ path: "/gallery", label: "Gallery", nav: true, group: "Product", real: true },
|
|
1678
|
-
{ path: "/events", label: "Private events", group: "Resources" }
|
|
1855
|
+
{ path: "/events", label: "Private events", group: "Resources", real: true }
|
|
1679
1856
|
]
|
|
1680
1857
|
};
|
|
1681
1858
|
return [...specific[k] ?? [], ...common];
|
|
@@ -2938,7 +3115,10 @@ export * from "./avatar";
|
|
|
2938
3115
|
function realPageFiles(ctx) {
|
|
2939
3116
|
const k = ctx.template.key;
|
|
2940
3117
|
const f = {};
|
|
2941
|
-
if (k !== "dashboard")
|
|
3118
|
+
if (k !== "dashboard") {
|
|
3119
|
+
f["app/faq/page.tsx"] = faqPage(ctx);
|
|
3120
|
+
f["app/careers/page.tsx"] = careersPage(ctx);
|
|
3121
|
+
}
|
|
2942
3122
|
if (k === "personal") {
|
|
2943
3123
|
f["app/work/page.tsx"] = workPage(ctx);
|
|
2944
3124
|
f["app/uses/page.tsx"] = usesPage(ctx);
|
|
@@ -2946,6 +3126,7 @@ function realPageFiles(ctx) {
|
|
|
2946
3126
|
if (k === "business") {
|
|
2947
3127
|
f["app/services/page.tsx"] = servicesPage(ctx);
|
|
2948
3128
|
f["app/pricing/page.tsx"] = pricingPage(ctx);
|
|
3129
|
+
f["app/work/page.tsx"] = businessWorkPage(ctx);
|
|
2949
3130
|
}
|
|
2950
3131
|
if (k === "saas") {
|
|
2951
3132
|
f["app/features/page.tsx"] = featuresPage(ctx);
|
|
@@ -2956,6 +3137,7 @@ function realPageFiles(ctx) {
|
|
|
2956
3137
|
if (k === "ecommerce") {
|
|
2957
3138
|
f["app/shop/page.tsx"] = shopPage(ctx);
|
|
2958
3139
|
f["app/cart/page.tsx"] = cartPage();
|
|
3140
|
+
f["app/collections/page.tsx"] = collectionsPage(ctx);
|
|
2959
3141
|
f["components/product-grid.tsx"] = productGrid();
|
|
2960
3142
|
f["components/cart-view.tsx"] = cartView();
|
|
2961
3143
|
}
|
|
@@ -2966,10 +3148,13 @@ function realPageFiles(ctx) {
|
|
|
2966
3148
|
if (k === "docs") {
|
|
2967
3149
|
f["app/guides/page.tsx"] = guidesPage();
|
|
2968
3150
|
f["app/changelog/page.tsx"] = changelogPage(ctx);
|
|
3151
|
+
f["app/api-reference/page.tsx"] = apiRefPage(ctx);
|
|
2969
3152
|
}
|
|
2970
3153
|
if (k === "restaurant") {
|
|
2971
3154
|
f["app/menu/page.tsx"] = menuPage(ctx);
|
|
2972
3155
|
f["app/gallery/page.tsx"] = galleryPage(ctx);
|
|
3156
|
+
f["app/reservations/page.tsx"] = reservationsPage(ctx);
|
|
3157
|
+
f["app/events/page.tsx"] = eventsPage(ctx);
|
|
2973
3158
|
}
|
|
2974
3159
|
if (k === "dashboard") {
|
|
2975
3160
|
f["app/settings/page.tsx"] = settingsPage(ctx);
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lacspace-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.1",
|
|
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",
|