create-ereo 0.2.6 → 0.2.8
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 +4 -0
- package/dist/index.js +88 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ bunx create-ereo@latest my-app --template minimal
|
|
|
81
81
|
| `--no-typescript` | | Use JavaScript instead of TypeScript |
|
|
82
82
|
| `--no-git` | | Skip git initialization |
|
|
83
83
|
| `--no-install` | | Skip package installation |
|
|
84
|
+
| `--trace` | | Include @ereo/trace for full-stack observability |
|
|
84
85
|
| `--help` | `-h` | Show help message |
|
|
85
86
|
|
|
86
87
|
## Examples
|
|
@@ -97,6 +98,9 @@ bunx create-ereo@latest my-app --no-install
|
|
|
97
98
|
|
|
98
99
|
# Create without git initialization
|
|
99
100
|
bunx create-ereo@latest my-app --no-git
|
|
101
|
+
|
|
102
|
+
# Create with tracing enabled
|
|
103
|
+
bunx create-ereo@latest my-app --trace
|
|
100
104
|
```
|
|
101
105
|
|
|
102
106
|
## Project Structure
|
package/dist/index.js
CHANGED
|
@@ -420,11 +420,28 @@ export default function HomePage() {
|
|
|
420
420
|
esModuleInterop: true,
|
|
421
421
|
skipLibCheck: true,
|
|
422
422
|
forceConsistentCasingInFileNames: true,
|
|
423
|
-
types: ["bun-types"]
|
|
423
|
+
types: ["bun-types"],
|
|
424
|
+
baseUrl: ".",
|
|
425
|
+
paths: {
|
|
426
|
+
"~/*": ["./app/*"],
|
|
427
|
+
"@/*": ["./app/*"]
|
|
428
|
+
}
|
|
424
429
|
},
|
|
425
430
|
include: ["app/**/*", "*.config.ts"]
|
|
426
431
|
};
|
|
427
432
|
await Bun.write(join(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
433
|
+
} else {
|
|
434
|
+
const jsconfig = {
|
|
435
|
+
compilerOptions: {
|
|
436
|
+
baseUrl: ".",
|
|
437
|
+
paths: {
|
|
438
|
+
"~/*": ["./app/*"],
|
|
439
|
+
"@/*": ["./app/*"]
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
include: ["app/**/*"]
|
|
443
|
+
};
|
|
444
|
+
await Bun.write(join(projectDir, "jsconfig.json"), JSON.stringify(jsconfig, null, 2));
|
|
428
445
|
}
|
|
429
446
|
await Bun.write(join(projectDir, ".gitignore"), `node_modules
|
|
430
447
|
.ereo
|
|
@@ -516,13 +533,27 @@ export default defineConfig({
|
|
|
516
533
|
skipLibCheck: true,
|
|
517
534
|
forceConsistentCasingInFileNames: true,
|
|
518
535
|
types: ["bun-types"],
|
|
536
|
+
baseUrl: ".",
|
|
519
537
|
paths: {
|
|
520
|
-
"~/*": ["./app/*"]
|
|
538
|
+
"~/*": ["./app/*"],
|
|
539
|
+
"@/*": ["./app/*"]
|
|
521
540
|
}
|
|
522
541
|
},
|
|
523
542
|
include: ["app/**/*", "*.config.ts"]
|
|
524
543
|
};
|
|
525
544
|
await Bun.write(join(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
545
|
+
} else {
|
|
546
|
+
const jsconfig = {
|
|
547
|
+
compilerOptions: {
|
|
548
|
+
baseUrl: ".",
|
|
549
|
+
paths: {
|
|
550
|
+
"~/*": ["./app/*"],
|
|
551
|
+
"@/*": ["./app/*"]
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
include: ["app/**/*"]
|
|
555
|
+
};
|
|
556
|
+
await Bun.write(join(projectDir, "jsconfig.json"), JSON.stringify(jsconfig, null, 2));
|
|
526
557
|
}
|
|
527
558
|
const tailwindConfig = `
|
|
528
559
|
/** @type {import('tailwindcss').Config} */
|
|
@@ -713,7 +744,7 @@ For production, alias \\\`@ereo/trace\\\` to \\\`@ereo/trace/noop\\\` \u2014 a 5
|
|
|
713
744
|
tags: ['ereo', 'tracing', 'devtools'],
|
|
714
745
|
},` : "";
|
|
715
746
|
const mockData = `
|
|
716
|
-
${ts ? `import type { Post } from '
|
|
747
|
+
${ts ? `import type { Post } from '~/lib/types';
|
|
717
748
|
` : ""}
|
|
718
749
|
/**
|
|
719
750
|
* Mock blog posts data.
|
|
@@ -859,10 +890,6 @@ export async function simulateDelay(ms${ts ? ": number" : ""} = 100)${ts ? ": Pr
|
|
|
859
890
|
`.trim();
|
|
860
891
|
await Bun.write(join(projectDir, `app/lib/data.${ts ? "ts" : "js"}`), mockData);
|
|
861
892
|
const navigation = `
|
|
862
|
-
'use client';
|
|
863
|
-
|
|
864
|
-
import { useState } from 'react';
|
|
865
|
-
|
|
866
893
|
const navLinks = [
|
|
867
894
|
{ href: '/', label: 'Home' },
|
|
868
895
|
{ href: '/blog', label: 'Blog' },
|
|
@@ -871,8 +898,6 @@ const navLinks = [
|
|
|
871
898
|
];
|
|
872
899
|
|
|
873
900
|
export function Navigation() {
|
|
874
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
875
|
-
|
|
876
901
|
return (
|
|
877
902
|
<nav className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
|
|
878
903
|
<div className="max-w-6xl mx-auto px-4">
|
|
@@ -905,37 +930,26 @@ export function Navigation() {
|
|
|
905
930
|
))}
|
|
906
931
|
</div>
|
|
907
932
|
|
|
908
|
-
{/* Mobile
|
|
909
|
-
<
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
aria-label="Toggle menu"
|
|
913
|
-
>
|
|
914
|
-
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
915
|
-
{isOpen ? (
|
|
916
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
917
|
-
) : (
|
|
933
|
+
{/* Mobile Navigation - uses native <details> for JS-free toggle */}
|
|
934
|
+
<details className="md:hidden relative">
|
|
935
|
+
<summary className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer list-none [&::-webkit-details-marker]:hidden">
|
|
936
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
918
937
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
919
|
-
|
|
920
|
-
</
|
|
921
|
-
|
|
938
|
+
</svg>
|
|
939
|
+
</summary>
|
|
940
|
+
<div className="absolute right-0 top-full mt-2 w-48 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50">
|
|
941
|
+
{navLinks.map((link) => (
|
|
942
|
+
<a
|
|
943
|
+
key={link.href}
|
|
944
|
+
href={link.href}
|
|
945
|
+
className="block px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-primary-600 hover:bg-gray-50 dark:hover:bg-gray-700"
|
|
946
|
+
>
|
|
947
|
+
{link.label}
|
|
948
|
+
</a>
|
|
949
|
+
))}
|
|
950
|
+
</div>
|
|
951
|
+
</details>
|
|
922
952
|
</div>
|
|
923
|
-
|
|
924
|
-
{/* Mobile Navigation */}
|
|
925
|
-
{isOpen && (
|
|
926
|
-
<div className="md:hidden py-4 border-t border-gray-200 dark:border-gray-800">
|
|
927
|
-
{navLinks.map((link) => (
|
|
928
|
-
<a
|
|
929
|
-
key={link.href}
|
|
930
|
-
href={link.href}
|
|
931
|
-
className="block py-2 text-gray-600 dark:text-gray-300 hover:text-primary-600"
|
|
932
|
-
onClick={() => setIsOpen(false)}
|
|
933
|
-
>
|
|
934
|
-
{link.label}
|
|
935
|
-
</a>
|
|
936
|
-
))}
|
|
937
|
-
</div>
|
|
938
|
-
)}
|
|
939
953
|
</div>
|
|
940
954
|
</nav>
|
|
941
955
|
);
|
|
@@ -1077,6 +1091,21 @@ export default function RootLayout({ children }${ts ? ": RootLayoutProps" : ""})
|
|
|
1077
1091
|
}
|
|
1078
1092
|
`.trim();
|
|
1079
1093
|
await Bun.write(join(projectDir, `app/routes/_layout.${ext}`), rootLayout);
|
|
1094
|
+
const clientEntry = `
|
|
1095
|
+
/**
|
|
1096
|
+
* Client Entry Point
|
|
1097
|
+
*
|
|
1098
|
+
* This file initializes the client-side runtime:
|
|
1099
|
+
* - Hydrates island components marked with 'use client'
|
|
1100
|
+
* - Sets up client-side navigation
|
|
1101
|
+
* - Enables link prefetching
|
|
1102
|
+
*/
|
|
1103
|
+
import { initClient } from '@ereo/client';
|
|
1104
|
+
|
|
1105
|
+
// Initialize the EreoJS client runtime
|
|
1106
|
+
initClient();
|
|
1107
|
+
`.trim();
|
|
1108
|
+
await Bun.write(join(projectDir, `app/entry.client.${ext}`), clientEntry);
|
|
1080
1109
|
const homePage = `
|
|
1081
1110
|
import { Counter } from '~/components/Counter';
|
|
1082
1111
|
import { getAllPosts, simulateDelay } from '~/lib/data';
|
|
@@ -1290,7 +1319,7 @@ export async function action({ request }) {
|
|
|
1290
1319
|
This counter is an island \u2014 it's the only part of this page that ships JavaScript. The rest is pure HTML from the server.
|
|
1291
1320
|
</p>
|
|
1292
1321
|
<div className="flex justify-center">
|
|
1293
|
-
<Counter initialCount={0} />
|
|
1322
|
+
<Counter client:load initialCount={0} />
|
|
1294
1323
|
</div>
|
|
1295
1324
|
</div>
|
|
1296
1325
|
</div>
|
|
@@ -1514,13 +1543,9 @@ export function ErrorBoundary({ error }${ts ? ": { error: Error }" : ""}) {
|
|
|
1514
1543
|
`.trim();
|
|
1515
1544
|
await Bun.write(join(projectDir, `app/routes/blog/[slug].${ext}`), blogPost);
|
|
1516
1545
|
const contactPage = `
|
|
1517
|
-
'use client';
|
|
1518
|
-
|
|
1519
|
-
import { useState } from 'react';
|
|
1520
|
-
|
|
1521
1546
|
/**
|
|
1522
1547
|
* Action handler for the contact form.
|
|
1523
|
-
* Runs on the server when the form is submitted.
|
|
1548
|
+
* Runs on the server when the form is submitted via POST.
|
|
1524
1549
|
*/
|
|
1525
1550
|
export async function action({ request }${ts ? ": { request: Request }" : ""}) {
|
|
1526
1551
|
const formData = await request.formData();
|
|
@@ -1568,13 +1593,6 @@ ${ts ? `interface ContactPageProps {
|
|
|
1568
1593
|
}
|
|
1569
1594
|
` : ""}
|
|
1570
1595
|
export default function ContactPage({ actionData }${ts ? ": ContactPageProps" : ""}) {
|
|
1571
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1572
|
-
|
|
1573
|
-
const handleSubmit = async (e${ts ? ": React.FormEvent<HTMLFormElement>" : ""}) => {
|
|
1574
|
-
setIsSubmitting(true);
|
|
1575
|
-
// Form will be handled by the action
|
|
1576
|
-
};
|
|
1577
|
-
|
|
1578
1596
|
return (
|
|
1579
1597
|
<div className="min-h-screen py-12 px-4">
|
|
1580
1598
|
<div className="max-w-2xl mx-auto">
|
|
@@ -1593,7 +1611,7 @@ export default function ContactPage({ actionData }${ts ? ": ContactPageProps" :
|
|
|
1593
1611
|
</div>
|
|
1594
1612
|
</div>
|
|
1595
1613
|
) : (
|
|
1596
|
-
<form method="POST"
|
|
1614
|
+
<form method="POST" className="space-y-6">
|
|
1597
1615
|
<div>
|
|
1598
1616
|
<label htmlFor="name" className="block text-sm font-medium mb-2">
|
|
1599
1617
|
Name
|
|
@@ -1647,10 +1665,9 @@ export default function ContactPage({ actionData }${ts ? ": ContactPageProps" :
|
|
|
1647
1665
|
|
|
1648
1666
|
<button
|
|
1649
1667
|
type="submit"
|
|
1650
|
-
|
|
1651
|
-
className="btn btn-primary w-full disabled:opacity-50"
|
|
1668
|
+
className="btn btn-primary w-full"
|
|
1652
1669
|
>
|
|
1653
|
-
|
|
1670
|
+
Send Message
|
|
1654
1671
|
</button>
|
|
1655
1672
|
</form>
|
|
1656
1673
|
)}
|
|
@@ -2051,13 +2068,27 @@ export default defineConfig({
|
|
|
2051
2068
|
skipLibCheck: true,
|
|
2052
2069
|
forceConsistentCasingInFileNames: true,
|
|
2053
2070
|
types: ["bun-types"],
|
|
2071
|
+
baseUrl: ".",
|
|
2054
2072
|
paths: {
|
|
2055
|
-
"~/*": ["./app/*"]
|
|
2073
|
+
"~/*": ["./app/*"],
|
|
2074
|
+
"@/*": ["./app/*"]
|
|
2056
2075
|
}
|
|
2057
2076
|
},
|
|
2058
2077
|
include: ["app/**/*", "*.config.ts"]
|
|
2059
2078
|
};
|
|
2060
2079
|
await Bun.write(join(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
2080
|
+
} else {
|
|
2081
|
+
const jsconfig = {
|
|
2082
|
+
compilerOptions: {
|
|
2083
|
+
baseUrl: ".",
|
|
2084
|
+
paths: {
|
|
2085
|
+
"~/*": ["./app/*"],
|
|
2086
|
+
"@/*": ["./app/*"]
|
|
2087
|
+
}
|
|
2088
|
+
},
|
|
2089
|
+
include: ["app/**/*"]
|
|
2090
|
+
};
|
|
2091
|
+
await Bun.write(join(projectDir, "jsconfig.json"), JSON.stringify(jsconfig, null, 2));
|
|
2061
2092
|
}
|
|
2062
2093
|
const tailwindConfig = `
|
|
2063
2094
|
/** @type {import('tailwindcss').Config} */
|
|
@@ -2341,7 +2372,7 @@ export default db;
|
|
|
2341
2372
|
* Shared types for the application.
|
|
2342
2373
|
*/
|
|
2343
2374
|
|
|
2344
|
-
export type { User, Task, TaskStats } from '
|
|
2375
|
+
export type { User, Task, TaskStats } from '~/lib/db';
|
|
2345
2376
|
|
|
2346
2377
|
export interface ActionResult<T = unknown> {
|
|
2347
2378
|
success: boolean;
|