@ludoloops/svelteforge 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/dist/index.js +100 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  ```bash
8
8
  # Full stack (UI + auth + DB)
9
- sv create my-app --template minimal --types ts --add tailwindcss @lelabdev/svelteforge
9
+ sv create my-app --template minimal --types ts --add tailwindcss @ludoloops/svelteforge
10
10
 
11
11
  # Or add to an existing project
12
12
  cd my-app
13
- sv add @lelabdev/svelteforge
13
+ sv add @ludoloops/svelteforge
14
14
  ```
15
15
 
16
16
  The addon prompts for a template mode:
@@ -139,7 +139,7 @@ svelteforge/ ← this repo (addon package)
139
139
  ├── scripts/
140
140
  │ └── prebuild.ts ← reads templates/ → generates src/templates.ts
141
141
  ├── tsdown.config.ts ← bundler (bundles everything into dist/index.js)
142
- ├── package.json ← @lelabdev/svelteforge
142
+ ├── package.json ← @ludoloops/svelteforge
143
143
  ├── AGENTS.md
144
144
  └── README.md
145
145
  ```
@@ -148,7 +148,7 @@ svelteforge/ ← this repo (addon package)
148
148
 
149
149
  1. `bun run prebuild` — reads `templates/` directories, inlines all file contents into `src/templates.ts`
150
150
  2. `tsdown` — bundles `src/index.ts` + modes + templates into a single `dist/index.js`
151
- 3. Published on npm as `@lelabdev/svelteforge`
151
+ 3. Published on npm as `@ludoloops/svelteforge`
152
152
 
153
153
  ## Development
154
154
 
@@ -159,7 +159,7 @@ bun run build
159
159
  # Test locally with bun link
160
160
  bun link
161
161
  mkdir /tmp/test-app && cd /tmp/test-app
162
- sv create my-app --template minimal --types ts --add tailwindcss @lelabdev/svelteforge
162
+ sv create my-app --template minimal --types ts --add tailwindcss @ludoloops/svelteforge
163
163
  cd my-app
164
164
  bun dev
165
165
  ```
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ const landingFiles = {
8
8
  "/lib/components/anim/DynamicCounter.svelte": "<script lang=\"ts\">\n import { onMount } from 'svelte';\n\n const {\n target = 0,\n duration = 2,\n suffix = '',\n prefix = '',\n once = true\n }: {\n target: number;\n duration?: number;\n suffix?: string;\n prefix?: string;\n once?: boolean;\n } = $props();\n\n let container: HTMLElement;\n let currentValue = $state(0);\n let hasAnimated = $state(false);\n\n onMount(() => {\n if (!container) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry.isIntersecting && (!hasAnimated || !once)) {\n hasAnimated = true;\n\n const startTime = performance.now();\n const animate = () => {\n const elapsed = performance.now() - startTime;\n const progress = Math.min(elapsed / (duration * 1000), 1);\n const easedProgress = 1 - Math.pow(1 - progress, 3);\n currentValue = Math.round(easedProgress * target);\n\n if (progress < 1) {\n requestAnimationFrame(animate);\n }\n };\n\n requestAnimationFrame(animate);\n\n if (once) {\n observer.disconnect();\n }\n } else if (!entry.isIntersecting && !once) {\n currentValue = 0;\n }\n },\n {\n threshold: 0.1,\n rootMargin: '0px 0px -10% 0px'\n }\n );\n\n observer.observe(container);\n return () => observer.disconnect();\n });\n<\/script>\n\n<span bind:this={container}>{prefix}{currentValue}{suffix}</span>\n",
9
9
  "/lib/components/layout/navbar.svelte": "<script lang=\"ts\">\n import { AppBar } from '@skeletonlabs/skeleton-svelte';\n import ThemeToggle from '$lib/components/ui/ThemeToggle.svelte';\n import { themeStore } from '$lib/utils/theme.svelte';\n import { onMount, onDestroy } from 'svelte';\n\n let mobileMenuOpen = $state(false);\n\n onMount(() => {\n themeStore.init();\n });\n onDestroy(() => {\n themeStore.destroy();\n });\n<\/script>\n\n{#if mobileMenuOpen}\n <div\n class=\"md:hidden fixed inset-0 top-16 bg-surface-50-900 z-40\"\n onclick={() => (mobileMenuOpen = false)}\n onkeydown={(e) => e.key === 'Escape' && (mobileMenuOpen = false)}\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n >\n <div class=\"flex flex-col p-6\">\n <button\n type=\"button\"\n onclick={() => {\n themeStore.toggle();\n mobileMenuOpen = false;\n }}\n class=\"flex items-center gap-3 px-4 py-3 rounded-xl hover:bg-surface-200-800 text-sm\"\n >\n {themeStore.isDark ? 'Light Mode' : 'Dark Mode'}\n </button>\n </div>\n </div>\n{/if}\n\n<AppBar>\n <AppBar.Toolbar class=\"grid-cols-[1fr_auto_1fr]\">\n <AppBar.Lead>\n <a\n href=\"/\"\n class=\"text-xl font-bold text-surface-50-950 hover:text-primary-400-500 transition-colors\"\n >\n __PROJECT_NAME__\n </a>\n </AppBar.Lead>\n\n <AppBar.Headline />\n\n <AppBar.Trail>\n <div class=\"hidden md:flex items-center gap-2\">\n <ThemeToggle />\n </div>\n\n <button\n onclick={() => (mobileMenuOpen = !mobileMenuOpen)}\n class=\"md:hidden btn-icon text-surface-50-950\"\n aria-label=\"Menu\"\n >\n {#if mobileMenuOpen}\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n {:else}\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path d=\"M3 12h18M3 6h18M3 18h18\" />\n </svg>\n {/if}\n </button>\n </AppBar.Trail>\n </AppBar.Toolbar>\n</AppBar>\n",
10
10
  "/lib/data/links.ts": "const links = {\n githubForge: {\n name: 'GitHub',\n href: 'https://github.com/lelabdev/svelteForge'\n },\n githubLudo: {\n name: 'GitHub',\n href: 'https://github.com/LudoLoops'\n },\n betterAuth: {\n name: 'Better Auth',\n href: 'https://better-auth.com'\n },\n shiki: {\n name: 'Shiki',\n href: 'https://shiki.style/'\n },\n kofi: {\n name: 'ko-fi',\n href: 'https://ko-fi.com/ludoloops'\n },\n lelab: {\n name: 'LeLab.dev',\n href: 'https://lelab.dev'\n },\n ludoloops: {\n name: 'LudoLoops',\n href: 'https://ludoloops.dev/'\n }\n};\nexport default links;\n",
11
- "/lib/sections/QuickStart.svelte": "<script lang=\"ts\">\n import { Badge, Card } from '$lib/components/ui';\n import Icon from '$lib/components/icons/Icon.svelte';\n\n const steps = [\n {\n number: '1',\n title: 'Create',\n description: 'One CLI command. Choose Landing or Full Stack.',\n icon: 'plus-circle'\n },\n {\n number: '2',\n title: 'Configure',\n description: 'Setup script generates .env, DB, admin. Zero files to edit.',\n icon: 'wrench'\n },\n {\n number: '3',\n title: 'Ship',\n description: 'Everything wired. Start coding what makes your project unique.',\n icon: 'rocket-launch'\n }\n ];\n\n const commands = [\n 'bunx sv create my-project --template minimal --types ts --add tailwindcss',\n 'cd my-project',\n 'bunx sv add @lelabdev/svelteforge=template:landing',\n 'bun run dev'\n ];\n<\/script>\n\n<section class=\"bg-surface-100-900 px-6 py-20\">\n <div class=\"mx-auto max-w-5xl\">\n <div class=\"mb-12 text-center\">\n <h2 class=\"title mb-4 text-4xl font-bold text-surface-950-50\">How it works</h2>\n <p class=\"subtitle text-xl text-surface-600-400\">Three steps. Not thirty.</p>\n </div>\n\n <!-- 3 Steps -->\n <div class=\"mb-12 grid gap-8 sm:grid-cols-3\">\n {#each steps as step (step)}\n <div class=\"text-center\">\n <div\n class=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary-500 text-xl font-bold text-white\"\n >\n {step.number}\n </div>\n <h3 class=\"title mb-2 text-xl font-semibold text-surface-950-50\">\n {step.title}\n </h3>\n <p class=\"subtitle text-surface-600-400\">{step.description}</p>\n </div>\n {/each}\n </div>\n\n <!-- Code Block -->\n <Card variant=\"flat\" class=\"bg-surface-800-900 p-0\" noPadding>\n <div class=\"p-8 font-mono text-sm text-surface-200-700\">\n {#each commands as cmd, i}\n <div class=\"mb-2 flex items-center gap-2\">\n <span class=\"text-surface-500\">➜</span>\n <span class=\"text-primary-400\">$</span>\n <span>{cmd}</span>\n </div>\n {/each}\n </div>\n </Card>\n\n <!-- Template Badges -->\n <div class=\"mt-12 text-center\">\n <p class=\"subtitle mb-6 text-lg text-surface-600-400\">Choose your template:</p>\n <div class=\"flex flex-wrap justify-center gap-3\">\n <Badge variant=\"primary\">Full Stack — Auth, DB, 40+ components</Badge>\n <Badge variant=\"surface\">Landing — Minimal, UI only</Badge>\n </div>\n </div>\n </div>\n</section>\n",
11
+ "/lib/sections/QuickStart.svelte": "<script lang=\"ts\">\n import { Badge, Card } from '$lib/components/ui';\n import Icon from '$lib/components/icons/Icon.svelte';\n\n const steps = [\n {\n number: '1',\n title: 'Create',\n description: 'One CLI command. Choose Landing or Full Stack.',\n icon: 'plus-circle'\n },\n {\n number: '2',\n title: 'Configure',\n description: 'Setup script generates .env, DB, admin. Zero files to edit.',\n icon: 'wrench'\n },\n {\n number: '3',\n title: 'Ship',\n description: 'Everything wired. Start coding what makes your project unique.',\n icon: 'rocket-launch'\n }\n ];\n\n const commands = [\n 'bunx sv create my-project --template minimal --types ts --add tailwindcss',\n 'cd my-project',\n 'bunx sv add @ludoloops/svelteforge=template:landing',\n 'bun run dev'\n ];\n<\/script>\n\n<section class=\"bg-surface-100-900 px-6 py-20\">\n <div class=\"mx-auto max-w-5xl\">\n <div class=\"mb-12 text-center\">\n <h2 class=\"title mb-4 text-4xl font-bold text-surface-950-50\">How it works</h2>\n <p class=\"subtitle text-xl text-surface-600-400\">Three steps. Not thirty.</p>\n </div>\n\n <!-- 3 Steps -->\n <div class=\"mb-12 grid gap-8 sm:grid-cols-3\">\n {#each steps as step (step)}\n <div class=\"text-center\">\n <div\n class=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary-500 text-xl font-bold text-white\"\n >\n {step.number}\n </div>\n <h3 class=\"title mb-2 text-xl font-semibold text-surface-950-50\">\n {step.title}\n </h3>\n <p class=\"subtitle text-surface-600-400\">{step.description}</p>\n </div>\n {/each}\n </div>\n\n <!-- Code Block -->\n <Card variant=\"flat\" class=\"bg-surface-800-900 p-0\" noPadding>\n <div class=\"p-8 font-mono text-sm text-surface-200-700\">\n {#each commands as cmd, i}\n <div class=\"mb-2 flex items-center gap-2\">\n <span class=\"text-surface-500\">➜</span>\n <span class=\"text-primary-400\">$</span>\n <span>{cmd}</span>\n </div>\n {/each}\n </div>\n </Card>\n\n <!-- Template Badges -->\n <div class=\"mt-12 text-center\">\n <p class=\"subtitle mb-6 text-lg text-surface-600-400\">Choose your template:</p>\n <div class=\"flex flex-wrap justify-center gap-3\">\n <Badge variant=\"primary\">Full Stack — Auth, DB, 40+ components</Badge>\n <Badge variant=\"surface\">Landing — Minimal, UI only</Badge>\n </div>\n </div>\n </div>\n</section>\n",
12
12
  "/lib/sections/Stats.svelte": "<script lang=\"ts\">\n import { Card } from '$lib/components/ui';\n import DynamicCounter from '$lib/components/anim/DynamicCounter.svelte';\n\n const stats = [\n { label: 'Hours Saved', suffix: '+', value: 20, description: 'On every new project', color: 'primary' },\n { label: 'Components', suffix: '+', value: 30, description: 'Ready to use', color: 'secondary' },\n { label: 'Lines of Code', suffix: '+', value: 5000, description: 'Pre-written for you', color: 'tertiary' },\n { label: 'Time to Deploy', value: 10, prefix: '<', suffix: 'min', description: 'From clone to production', color: 'success' }\n ];\n<\/script>\n\n<section class=\"relative bg-surface-100-900 px-6 py-16\">\n <div class=\"mx-auto max-w-7xl\">\n <div class=\"mb-12 text-center\">\n <h2 class=\"title mb-3 text-2xl font-bold text-surface-950-50 sm:text-3xl\">You know the routine.</h2>\n <p class=\"subtitle text-surface-600-400\">Every project, the same thing.</p>\n </div>\n\n <div class=\"grid gap-8 sm:grid-cols-2 lg:grid-cols-4\">\n {#each stats as stat, i (stat)}\n <Card variant=\"flat\" class=\"border-t-4 p-8 text-center border-{stat.color}-500\">\n <span class=\"title mb-2 block text-4xl font-bold text-{stat.color}-500\">\n {stat.prefix ?? ''}<DynamicCounter suffix={stat.suffix} target={stat.value} duration={3} />\n </span>\n <div class=\"mb-1 text-xl font-semibold text-surface-900-50\">{stat.label}</div>\n <div class=\"text-sm text-surface-600-400\">{stat.description}</div>\n </Card>\n {/each}\n </div>\n\n <div class=\"mt-10 text-center\">\n <p class=\"subtitle mb-4 text-lg text-surface-600-400\">\n 15 hours rebuilding what already exists. And you haven't even started your product.\n </p>\n <p class=\"title text-xl font-bold text-primary-500\">\n What if all of this took 2 minutes?\n </p>\n </div>\n </div>\n</section>\n",
13
13
  "/lib/sections/UseCase.svelte": "<script>\n import { Card } from '$lib/components/ui';\n import Icon from '$lib/components/icons/Icon.svelte';\n\n const points = [\n {\n icon: 'shieldCheck',\n title: 'MIT license — use it however you want',\n borderColor: 'border-primary-500',\n iconBg: 'bg-primary-400-600/40',\n textColor: 'text-primary-500'\n },\n {\n icon: 'arrowSquareOut',\n title: 'No vendor lock-in — it\\'s your codebase',\n borderColor: 'border-secondary-500',\n iconBg: 'bg-secondary-400-600/40',\n textColor: 'text-secondary-500'\n },\n {\n icon: 'clock',\n title: 'Built on sv create — you follow SvelteKit, not a fork',\n borderColor: 'border-tertiary-500',\n iconBg: 'bg-tertiary-400-600/40',\n textColor: 'text-tertiary-500'\n },\n {\n icon: 'airplane',\n title: 'Svelte community — one niche, done right',\n borderColor: 'border-success-500',\n iconBg: 'bg-success-400-600/40',\n textColor: 'text-success-500'\n }\n ];\n<\/script>\n\n<section class=\"relative bg-surface-50 px-6 py-20\">\n <div class=\"mx-auto max-w-4xl\">\n <div class=\"mb-16 text-center\">\n <h2 class=\"title mb-4 text-4xl font-bold text-surface-950-50\">Open source. For real.</h2>\n <p class=\"subtitle text-xl text-surface-600\">\n No hidden freemium, no surprise at upgrade time. The code is there, it stays there.\n </p>\n </div>\n\n <div class=\"grid gap-6 sm:grid-cols-2\">\n {#each points as point (point)}\n <Card variant=\"flat\" class=\"border-l-4 {point.borderColor} bg-surface-100-900 p-6 transition-all hover:scale-105 hover:shadow-lg\">\n <div class=\"flex items-start gap-4\">\n <div class=\"rounded-lg {point.iconBg} p-3\">\n <Icon name={point.icon} size={24} class={point.textColor} />\n </div>\n <p class=\"subtitle text-lg font-medium text-surface-950-50\">{point.title}</p>\n </div>\n </Card>\n {/each}\n </div>\n </div>\n</section>\n",
14
14
  "/lib/sections/Include.svelte": "<script lang=\"ts\">\n import { Card } from '$lib/components/ui';\n\n // Define the data structure for the cards\n type IncludeItem = {\n title: string;\n description: string;\n };\n\n // Items to keep\n const keepItems: IncludeItem[] = [\n {\n title: 'Authentication System',\n description: '- /login, /register, hooks.server.ts'\n },\n {\n title: 'UI Component Library',\n description: '- src/lib/components/ui/'\n },\n {\n title: 'Database Setup',\n description: '- src/lib/server/db/'\n },\n {\n title: 'Security Helpers',\n description: '- src/lib/server/security.ts'\n },\n {\n title: 'Type Definitions',\n description: '- src/app.d.ts'\n }\n ];\n\n // Items to delete\n const deleteItems: IncludeItem[] = [\n {\n title: '/demo-ui route',\n description: '- Delete after exploring components'\n },\n {\n title: '/carta route',\n description: '- Delete if not using Markdown'\n },\n {\n title: '/dashboard & /admin',\n description: '- Example protected routes'\n },\n {\n title: 'src/lib/components/Carta/',\n description: '- If not using Markdown editor'\n },\n {\n title: 'src/lib/components/SortDnD/',\n description: '- If not using drag & drop'\n }\n ];\n<\/script>\n\n<section class=\"bg-surface-100 px-6 py-20\">\n <div class=\"mx-auto max-w-7xl\">\n <div class=\"grid gap-12 lg:grid-cols-2\">\n <!-- What to Keep column -->\n <div>\n <div class=\"mb-6 flex items-center gap-3\">\n <div class=\"rounded-full bg-success-500/20 p-2\">\n <div class=\"h-3 w-3 rounded-full bg-success-500\"></div>\n </div>\n <h2 class=\"text-3xl font-bold text-surface-900-50\">✅ What to Keep</h2>\n </div>\n <div class=\"space-y-3\">\n {#each keepItems as item, index (index)}\n <Card\n variant=\"flat\"\n class=\"border-l-4 border-success-500 bg-success-50-950/80 p-4 transition-all hover:scale-102 hover:shadow-md\"\n >\n <strong class=\"text-success-700-300\">{item.title}</strong>\n <span class=\"text-surface-600-400\">{item.description}</span>\n </Card>\n {/each}\n </div>\n </div>\n\n <!-- What to Delete column -->\n <div>\n <div class=\"mb-6 flex items-center gap-3\">\n <div class=\"rounded-full bg-error-500/20 p-2\">\n <div class=\"h-3 w-3 rounded-full bg-error-500\"></div>\n </div>\n <h2 class=\"text-3xl font-bold text-surface-950-50\">❌ What to Delete (Examples)</h2>\n </div>\n <div class=\"space-y-3\">\n {#each deleteItems as item, index (index)}\n <Card\n variant=\"flat\"\n class=\"border-l-4 border-error-500 bg-error-200-800/40 p-4 transition-all hover:scale-102 hover:shadow-md\"\n >\n <strong class=\"text-error-700-300\">{item.title}</strong>\n <span class=\"text-surface-600-400\">{item.description}</span>\n </Card>\n {/each}\n </div>\n </div>\n </div>\n </div>\n</section>\n",
@@ -156,54 +156,123 @@ const fullstackFiles = {
156
156
  * Apply Landing mode files via sv.file()
157
157
  * Landing = UI base kit for building a landing page
158
158
  */
159
+ const UI_SKIP = [
160
+ "AuthCard",
161
+ "DataTable",
162
+ "NavigationLoader",
163
+ "NotificationBadge",
164
+ "SearchInput",
165
+ ".test.ts"
166
+ ];
167
+ const LAYOUT_SKIP = ["auth-buttons", "AdminSidebar"];
168
+ function shouldSkipUi(name) {
169
+ return UI_SKIP.some((s) => name.startsWith(s) || name.endsWith(s));
170
+ }
171
+ function shouldSkipLayout(name) {
172
+ return LAYOUT_SKIP.some((s) => name.startsWith(s));
173
+ }
174
+ /**
175
+ * Generate a barrel index.ts that only exports files present in the filtered set
176
+ */
177
+ function generateBarrel(files, directory, stripExtension = true) {
178
+ const lines = [];
179
+ const sortedPaths = [...files.keys()].filter((p) => p.startsWith(directory)).sort();
180
+ for (const path of sortedPaths) {
181
+ const name = path.split("/").pop() || "";
182
+ if (name === "index.ts" || name.endsWith(".test.ts")) continue;
183
+ const baseName = stripExtension ? name.replace(/\.\w+$/, "") : name;
184
+ if (name.endsWith(".svelte")) {
185
+ const componentName = baseName;
186
+ lines.push(`export { default as ${componentName} } from './${name}';`);
187
+ } else if (name.endsWith(".ts")) lines.push(`export * from './${name}';`);
188
+ }
189
+ return lines.join("\n") + "\n";
190
+ }
159
191
  function applyLandingMode(sv, landingFiles, fullstackFiles, projectName) {
160
- const sharedPaths = Object.entries(fullstackFiles).filter(([path]) => {
192
+ const includedFiles = /* @__PURE__ */ new Map();
193
+ const uiFiles = /* @__PURE__ */ new Map();
194
+ const uiFormFiles = /* @__PURE__ */ new Map();
195
+ const uiRichTextFiles = /* @__PURE__ */ new Map();
196
+ const layoutFiles = /* @__PURE__ */ new Map();
197
+ for (const [path, content] of Object.entries(fullstackFiles)) {
161
198
  if (path.startsWith("/lib/components/ui/")) {
162
- const name = path.split("/").pop() || "";
163
- if ([
164
- "AuthCard",
165
- "DataTable",
166
- "NavigationLoader",
167
- "NotificationBadge",
168
- "SearchInput",
169
- ".test.ts"
170
- ].some((s) => name.startsWith(s) || name.endsWith(s))) return false;
171
- return true;
199
+ if (shouldSkipUi(path.split("/").pop() || "")) continue;
200
+ includedFiles.set(path, content);
201
+ if (path.startsWith("/lib/components/ui/form/") && !path.endsWith("index.ts")) uiFormFiles.set(path, content);
202
+ else if (path.startsWith("/lib/components/ui/rich-text/") && !path.endsWith("index.ts")) uiRichTextFiles.set(path, content);
203
+ else if (!path.endsWith("index.ts")) uiFiles.set(path, content);
204
+ continue;
172
205
  }
173
206
  if (path.startsWith("/lib/components/layout/")) {
174
- const name = path.split("/").pop() || "";
175
- if (["auth-buttons", "AdminSidebar"].some((s) => name.startsWith(s))) return false;
176
- return true;
207
+ if (shouldSkipLayout(path.split("/").pop() || "")) continue;
208
+ includedFiles.set(path, content);
209
+ if (!path.endsWith("index.ts")) layoutFiles.set(path, content);
210
+ continue;
211
+ }
212
+ if (path === "/lib/components/ui/index.ts" || path === "/lib/components/ui/form/index.ts" || path === "/lib/components/layout/index.ts" || path === "/lib/components/index.ts") continue;
213
+ if (path.startsWith("/lib/components/icons/")) {
214
+ includedFiles.set(path, content);
215
+ continue;
216
+ }
217
+ if (path.startsWith("/lib/styles/")) {
218
+ includedFiles.set(path, content);
219
+ continue;
177
220
  }
178
- if (path.startsWith("/lib/components/icons/")) return true;
179
- if (path === "/lib/components/index.ts") return true;
180
- if (path.startsWith("/lib/components/layout/index.ts")) return true;
181
- if (path.startsWith("/lib/components/ui/index.ts")) return true;
182
- if (path.startsWith("/lib/components/ui/form/index.ts")) return true;
183
- if (path.startsWith("/lib/styles/")) return true;
184
221
  if (path.startsWith("/lib/utils/")) {
185
222
  const name = path.split("/").pop() || "";
186
- if (name.endsWith(".test.ts")) return false;
223
+ if (name.endsWith(".test.ts")) continue;
187
224
  if ([
188
225
  "export.ts",
189
226
  "slugify.ts",
190
227
  "form-errors.ts"
191
- ].includes(name)) return false;
192
- return true;
228
+ ].includes(name)) continue;
229
+ includedFiles.set(path, content);
230
+ continue;
193
231
  }
194
232
  if ([
195
233
  "/lib/errors.ts",
196
234
  "/lib/logger.ts",
197
235
  "/lib/types.ts",
198
236
  "/lib/index.ts"
199
- ].includes(path)) return true;
200
- if (path.startsWith("/lib/schemas/")) return true;
201
- if (["/app.css", "/app.html"].includes(path)) return true;
202
- if (path.startsWith("/routes/(legal)/")) return true;
203
- if (path === "/routes/+error.svelte") return true;
204
- return false;
237
+ ].includes(path)) {
238
+ includedFiles.set(path, content);
239
+ continue;
240
+ }
241
+ if (path.startsWith("/lib/schemas/")) {
242
+ includedFiles.set(path, content);
243
+ continue;
244
+ }
245
+ if (["/app.css", "/app.html"].includes(path)) {
246
+ includedFiles.set(path, content);
247
+ continue;
248
+ }
249
+ if (path.startsWith("/routes/(legal)/")) {
250
+ includedFiles.set(path, content);
251
+ continue;
252
+ }
253
+ if (path === "/routes/+error.svelte") {
254
+ includedFiles.set(path, content);
255
+ continue;
256
+ }
257
+ }
258
+ for (const [path, content] of includedFiles) sv.file(`src${path}`, () => content);
259
+ sv.file("src/lib/components/ui/index.ts", () => {
260
+ let barrel = generateBarrel(uiFiles, "/lib/components/ui/");
261
+ if (uiRichTextFiles.size > 0) barrel += "\n// === Rich Text ===\nexport * from './rich-text';\n";
262
+ if (uiFormFiles.size > 0) barrel += "\n// === Form ===\nexport * from './form';\n";
263
+ return barrel;
264
+ });
265
+ sv.file("src/lib/components/ui/form/index.ts", () => {
266
+ const formComponents = [];
267
+ for (const path of [...uiFormFiles.keys()].sort()) {
268
+ const baseName = (path.split("/").pop() || "").replace(".svelte", "");
269
+ formComponents.push(baseName);
270
+ }
271
+ return `${formComponents.map((c) => `import ${c} from './${c}.svelte';`).join("\n")}\n\nexport { ${formComponents.join(", ")} };\n`;
272
+ });
273
+ sv.file("src/lib/components/layout/index.ts", () => {
274
+ return generateBarrel(layoutFiles, "/lib/components/layout/");
205
275
  });
206
- for (const [path, content] of sharedPaths) sv.file(`src${path}`, () => content);
207
276
  for (const [path, content] of Object.entries(landingFiles)) {
208
277
  const finalContent = content.replace(/__PROJECT_NAME__/g, projectName);
209
278
  sv.file(`src${path}`, () => finalContent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ludoloops/svelteforge",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "sv community addon — 34 theme-aware UI components, admin dashboard, and 3-layer theme system for SvelteKit",
6
6
  "author": "Ludo (https://lelab.dev)",