@emberkit/cli 0.5.2 → 0.6.1-alpha.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.
Files changed (47) hide show
  1. package/dist/cli.js +44 -10
  2. package/dist/commands/create.js +10 -21
  3. package/dist/templates/action/index.js +12 -0
  4. package/dist/templates/action.js +12 -0
  5. package/dist/templates/api/api.js +280 -0
  6. package/dist/templates/apiRoute/index.js +20 -0
  7. package/dist/templates/apiRoute.js +20 -0
  8. package/dist/templates/blog/blog.js +358 -0
  9. package/dist/templates/component/index.js +14 -0
  10. package/dist/templates/component.js +14 -0
  11. package/dist/templates/config/index.js +13 -0
  12. package/dist/templates/config.js +13 -0
  13. package/dist/templates/context/index.js +21 -0
  14. package/dist/templates/context.js +21 -0
  15. package/dist/templates/dashboard/dashboard.js +406 -0
  16. package/dist/templates/entry/index.js +15 -0
  17. package/dist/templates/entry.js +15 -0
  18. package/dist/templates/errorBoundary/index.js +17 -0
  19. package/dist/templates/errorBoundary.js +17 -0
  20. package/dist/templates/form/index.js +59 -0
  21. package/dist/templates/form.js +59 -0
  22. package/dist/templates/index.js +0 -260
  23. package/dist/templates/layout/index.js +16 -0
  24. package/dist/templates/layout.js +16 -0
  25. package/dist/templates/layoutRoutes/index.js +9 -0
  26. package/dist/templates/layoutRoutes.js +9 -0
  27. package/dist/templates/loader/index.js +10 -0
  28. package/dist/templates/loader.js +10 -0
  29. package/dist/templates/minimal/minimal.js +89 -0
  30. package/dist/templates/project-templates/_shared/base.js +139 -0
  31. package/dist/templates/project-templates/api/api.js +209 -0
  32. package/dist/templates/project-templates/blog/blog.js +283 -0
  33. package/dist/templates/project-templates/dashboard/dashboard.js +331 -0
  34. package/dist/templates/project-templates/minimal/minimal.js +21 -0
  35. package/dist/templates/project-templates/saas/saas.js +461 -0
  36. package/dist/templates/project-templates/starter-kit/starter.js +209 -0
  37. package/dist/templates/project-templates/starter-kit/with-ui.js +365 -0
  38. package/dist/templates/projects/starter.js +286 -0
  39. package/dist/templates/projects/with-ui.js +445 -0
  40. package/dist/templates/projects.js +419 -191
  41. package/dist/templates/route/index.js +12 -0
  42. package/dist/templates/route.js +12 -0
  43. package/dist/templates/saas/saas.js +539 -0
  44. package/dist/templates/signal/index.js +25 -0
  45. package/dist/templates/signal.js +25 -0
  46. package/dist/utils/generator.js +30 -2
  47. package/package.json +1 -2
@@ -1,263 +1,3 @@
1
- export const routeTemplate = `import type { RouteComponent } from '@emberkit/core';
2
-
3
- const {{name}}: RouteComponent = () => {
4
- return (
5
- <div>
6
- <h1>{{name}}</h1>
7
- </div>
8
- );
9
- };
10
-
11
- export default {{name}};
12
- `;
13
- export const componentTemplate = `interface {{name}}Props {
14
- className?: string;
15
- }
16
-
17
- const {{name}} = ({ className = '' }: {{name}}Props) => {
18
- return (
19
- <div className={className}>
20
- {{name}} component
21
- </div>
22
- );
23
- };
24
-
25
- export default {{name}};
26
- `;
27
- export const layoutTemplate = `import type { RouteComponent } from '@emberkit/core';
28
-
29
- const {{name}}Layout: RouteComponent = ({ children }) => {
30
- return (
31
- <div>
32
- <header>
33
- <nav>{{name}} Navigation</nav>
34
- </header>
35
- <main>{children}</main>
36
- <footer>Footer</footer>
37
- </div>
38
- );
39
- };
40
-
41
- export default {{name}}Layout;
42
- `;
43
- export const errorBoundaryTemplate = `import type { RouteComponent } from '@emberkit/core';
44
-
45
- interface {{name}}ErrorProps {
46
- error: Error;
47
- }
48
-
49
- const {{name}}Error: RouteComponent<{{name}}ErrorProps> = ({ error }) => {
50
- return (
51
- <div className="error-boundary">
52
- <h2>Something went wrong</h2>
53
- <p>{error.message}</p>
54
- </div>
55
- );
56
- };
57
-
58
- export default {{name}}Error;
59
- `;
60
- export const loaderTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
61
-
62
- export const loader: LoaderFunction = async ({ params, query, request }) => {
63
- return {
64
- data: {
65
- // Add your data here
66
- },
67
- } as LoaderResult<unknown>;
68
- };
69
- `;
70
- export const actionTemplate = `import type { ActionFunction, LoaderResult } from '@emberkit/core';
71
-
72
- export const action: ActionFunction = async ({ params, request }) => {
73
- const formData = await request.formData();
74
-
75
- return {
76
- data: {
77
- success: true,
78
- },
79
- } as LoaderResult<unknown>;
80
- };
81
- `;
82
- export const apiRouteTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
83
-
84
- export const GET: LoaderFunction = async ({ params, query, request }) => {
85
- return {
86
- data: {
87
- message: 'Hello from API',
88
- },
89
- } as LoaderResult<unknown>;
90
- };
91
-
92
- export const POST: LoaderFunction = async ({ request }) => {
93
- const body = await request.json();
94
-
95
- return {
96
- data: {
97
- received: body,
98
- },
99
- } as LoaderResult<unknown>;
100
- };
101
- `;
102
- export const configTemplate = `import { defineConfig } from '@emberkit/core';
103
-
104
- export default defineConfig({
105
- mode: 'hybrid',
106
- server: {
107
- port: 3000,
108
- },
109
- build: {
110
- outDir: 'dist',
111
- target: 'esnext',
112
- },
113
- });
114
- `;
115
- export const indexTemplate = `import { render } from '@emberkit/core';
116
- import { routes } from 'virtual:emberkit-routes';
117
- import App from './routes/_layout';
118
- import './styles.css';
119
-
120
- const root = document.getElementById('app');
121
-
122
- if (root) {
123
- try {
124
- render(App, root, { routes });
125
- } catch (error) {
126
- console.error('[entry] Render error:', error);
127
- }
128
- }
129
- `;
130
- export const layoutRoutesTemplate = `// EmberKit uses file-based routing.
131
- // Routes are automatically discovered from the src/routes directory.
132
- // - src/routes/index.tsx → /
133
- // - src/routes/about.tsx → /about
134
- // - src/routes/[slug].tsx → /:slug
135
- // - src/routes/[...rest].tsx → catch-all
136
-
137
- export {};
138
- `;
139
- export const signalTemplate = `import { signal, computed, effect } from '@emberkit/core';
140
-
141
- // Writable signal
142
- const count = signal(0);
143
-
144
- // Computed value
145
- const doubled = computed(() => count.value * 2);
146
-
147
- // Side effect
148
- effect(() => {
149
- console.log('Count changed to:', count.value);
150
- });
151
-
152
- // Update
153
- count.value++;
154
-
155
- // Batch updates
156
- import { batch } from '@emberkit/core';
157
-
158
- batch(() => {
159
- count.value = 10;
160
- });
161
-
162
- export { count, doubled };
163
- `;
164
- export const contextTemplate = `import { createContext, useContext } from '@emberkit/core';
165
-
166
- interface {{name}}Context {
167
- // Define your context shape
168
- value: string;
169
- }
170
-
171
- const {{name}}Context = createContext<{{name}}Context>({
172
- value: 'default',
173
- });
174
-
175
- // Provider usage:
176
- // <{{name}}Context.Provider value={{ value: 'hello' }}>
177
- // {children}
178
- // </{{name}}Context.Provider>
179
-
180
- // Consumer usage:
181
- // const ctx = useContext({{name}}Context);
182
-
183
- export { {{name}}Context };
184
- `;
185
- export const formTemplate = `import { signal } from '@emberkit/core';
186
-
187
- const {{name}}Form = () => {
188
- const email = signal('');
189
- const password = signal('');
190
- const error = signal<string | null>(null);
191
- const loading = signal(false);
192
-
193
- const handleSubmit = async (e: Event) => {
194
- e.preventDefault();
195
- error.value = null;
196
- loading.value = true;
197
-
198
- try {
199
- const response = await fetch('/api/auth/login', {
200
- method: 'POST',
201
- headers: { 'Content-Type': 'application/json' },
202
- body: JSON.stringify({
203
- email: email.value,
204
- password: password.value,
205
- }),
206
- });
207
-
208
- if (!response.ok) {
209
- throw new Error('Login failed');
210
- }
211
-
212
- // Handle success
213
- } catch (err) {
214
- error.value = err instanceof Error ? err.message : 'Unknown error';
215
- } finally {
216
- loading.value = false;
217
- }
218
- };
219
-
220
- return (
221
- <form onSubmit={handleSubmit}>
222
- <input
223
- type="email"
224
- value={email.value}
225
- onInput={(e) => { email.value = e.currentTarget.value; }}
226
- placeholder="Email"
227
- />
228
- <input
229
- type="password"
230
- value={password.value}
231
- onInput={(e) => { password.value = e.currentTarget.value; }}
232
- placeholder="Password"
233
- />
234
- {error.value && <p className="text-red-500">{error.value}</p>}
235
- <button type="submit" disabled={loading.value}>
236
- {loading.value ? 'Loading...' : 'Submit'}
237
- </button>
238
- </form>
239
- );
240
- };
241
-
242
- export default {{name}}Form;
243
- `;
244
- export function getTemplate(type) {
245
- const templates = {
246
- route: routeTemplate,
247
- component: componentTemplate,
248
- layout: layoutTemplate,
249
- error: errorBoundaryTemplate,
250
- loader: loaderTemplate,
251
- action: actionTemplate,
252
- api: apiRouteTemplate,
253
- config: configTemplate,
254
- index: indexTemplate,
255
- signal: signalTemplate,
256
- context: contextTemplate,
257
- form: formTemplate,
258
- };
259
- return templates[type] ?? routeTemplate;
260
- }
261
1
  export function formatTemplate(template, params) {
262
2
  let result = template;
263
3
  for (const [key, value] of Object.entries(params)) {
@@ -0,0 +1,16 @@
1
+ export const layoutTemplate = `import type { RouteComponent } from '@emberkit/core';
2
+
3
+ const {{name}}Layout: RouteComponent = ({ children }) => {
4
+ return (
5
+ <div>
6
+ <header>
7
+ <nav>{{name}} Navigation</nav>
8
+ </header>
9
+ <main>{children}</main>
10
+ <footer>Footer</footer>
11
+ </div>
12
+ );
13
+ };
14
+
15
+ export default {{name}}Layout;
16
+ `;
@@ -0,0 +1,16 @@
1
+ export const layoutTemplate = `import type { RouteComponent } from '@emberkit/core';
2
+
3
+ const {{name}}Layout: RouteComponent = ({ children }) => {
4
+ return (
5
+ <div>
6
+ <header>
7
+ <nav>{{name}} Navigation</nav>
8
+ </header>
9
+ <main>{children}</main>
10
+ <footer>Footer</footer>
11
+ </div>
12
+ );
13
+ };
14
+
15
+ export default {{name}}Layout;
16
+ `;
@@ -0,0 +1,9 @@
1
+ export const layoutRoutesTemplate = `// EmberKit uses file-based routing.
2
+ // Routes are automatically discovered from the src/routes directory.
3
+ // - src/routes/index.tsx → /
4
+ // - src/routes/about.tsx → /about
5
+ // - src/routes/[slug].tsx → /:slug
6
+ // - src/routes/[...rest].tsx → catch-all
7
+
8
+ export {};
9
+ `;
@@ -0,0 +1,9 @@
1
+ export const layoutRoutesTemplate = `// EmberKit uses file-based routing.
2
+ // Routes are automatically discovered from the src/routes directory.
3
+ // - src/routes/index.tsx → /
4
+ // - src/routes/about.tsx → /about
5
+ // - src/routes/[slug].tsx → /:slug
6
+ // - src/routes/[...rest].tsx → catch-all
7
+
8
+ export {};
9
+ `;
@@ -0,0 +1,10 @@
1
+ export const loaderTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
2
+
3
+ export const loader: LoaderFunction = async ({ params, query, request }) => {
4
+ return {
5
+ data: {
6
+ // Add your data here
7
+ },
8
+ } as LoaderResult<unknown>;
9
+ };
10
+ `;
@@ -0,0 +1,10 @@
1
+ export const loaderTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
2
+
3
+ export const loader: LoaderFunction = async ({ params, query, request }) => {
4
+ return {
5
+ data: {
6
+ // Add your data here
7
+ },
8
+ } as LoaderResult<unknown>;
9
+ };
10
+ `;
@@ -0,0 +1,89 @@
1
+ export const minimalTemplate = {
2
+ "package.json": `{
3
+ "name": "{{name}}",
4
+ "version": "0.1.0",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "emberkit dev",
9
+ "build": "emberkit build",
10
+ "preview": "emberkit preview"
11
+ },
12
+ "dependencies": {
13
+ "@emberkit/core": "^0.2.4"
14
+ },
15
+ "devDependencies": {
16
+ "@emberkit/cli": "^0.2.4",
17
+ "typescript": "^5.7.0",
18
+ "vite": "^6.0.0"
19
+ }
20
+ }`,
21
+ "tsconfig.json": `{
22
+ "compilerOptions": {
23
+ "target": "ES2022",
24
+ "module": "ESNext",
25
+ "moduleResolution": "bundler",
26
+ "jsx": "react-jsx",
27
+ "jsxImportSource": "@emberkit/core",
28
+ "strict": true,
29
+ "esModuleInterop": true,
30
+ "skipLibCheck": true,
31
+ "forceConsistentCasingInFileNames": true,
32
+ "resolveJsonModule": true,
33
+ "isolatedModules": true,
34
+ "noEmit": true,
35
+ "lib": ["ES2022", "DOM", "DOM.Iterable"]
36
+ },
37
+ "include": ["src"],
38
+ "exclude": ["node_modules", "dist"]
39
+ }`,
40
+ "vite.config.ts": `import { defineConfig } from 'vite';
41
+ import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
42
+
43
+ export default defineConfig({
44
+ plugins: [emberkitVitePlugin()],
45
+ server: {
46
+ port: 3000,
47
+ host: 'localhost',
48
+ },
49
+ esbuild: {
50
+ jsxImportSource: '@emberkit/core',
51
+ },
52
+ });`,
53
+ "index.html": `<!DOCTYPE html>
54
+ <html lang="en">
55
+ <head>
56
+ <meta charset="UTF-8">
57
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
58
+ <title>{{name}}</title>
59
+ </head>
60
+ <body id="app">
61
+ <script type="module" src="/src/index.tsx"></script>
62
+ </body>
63
+ </html>`,
64
+ "src/index.tsx": `import { render } from '@emberkit/core';
65
+ import { routes } from 'virtual:emberkit-routes';
66
+ import App from './routes/index';
67
+
68
+ const root = document.getElementById('app');
69
+
70
+ if (root) {
71
+ try {
72
+ render(App, root, { routes });
73
+ } catch (error) {
74
+ console.error('[entry] Render error:', error);
75
+ }
76
+ }`,
77
+ "src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
78
+
79
+ const Home: RouteComponent = () => {
80
+ return (
81
+ <div style={{ fontFamily: 'system-ui, sans-serif', maxWidth: '600px', margin: '2rem auto', padding: '0 1rem' }}>
82
+ <h1>{{name}}</h1>
83
+ <p>Built with EmberKit</p>
84
+ </div>
85
+ );
86
+ };
87
+
88
+ export default Home;`,
89
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Shared builders for project template boilerplate.
3
+ * Each template composes from these instead of duplicating identical config files.
4
+ */
5
+ export function buildPackageJson(options = {}) {
6
+ const { hasTailwind = false, hasUI = false } = options;
7
+ const deps = { "@emberkit/core": "^0.2.4" };
8
+ if (hasUI)
9
+ deps["@emberkit/ui"] = "^0.2.3";
10
+ const devDeps = {
11
+ "@emberkit/cli": "^0.2.4",
12
+ typescript: "^5.7.0",
13
+ vite: "^6.0.0",
14
+ };
15
+ if (hasTailwind) {
16
+ devDeps["tailwindcss"] = "^4.0.0";
17
+ devDeps["@tailwindcss/vite"] = "^4.0.0";
18
+ }
19
+ return JSON.stringify({
20
+ name: "{{name}}",
21
+ version: "0.1.0",
22
+ private: true,
23
+ type: "module",
24
+ scripts: {
25
+ dev: "emberkit dev",
26
+ build: "emberkit build",
27
+ preview: "emberkit preview",
28
+ lint: "eslint src --ext .ts,.tsx",
29
+ format: 'prettier --write "src/**/*.{ts,tsx}"',
30
+ },
31
+ dependencies: deps,
32
+ devDependencies: devDeps,
33
+ }, null, 2);
34
+ }
35
+ export function buildTsConfig(hasPaths = true) {
36
+ const pathsEntry = hasPaths
37
+ ? `,
38
+ "paths": {
39
+ "@/*": ["./src/*"]
40
+ }`
41
+ : "";
42
+ return `{
43
+ "compilerOptions": {
44
+ "target": "ES2022",
45
+ "module": "ESNext",
46
+ "moduleResolution": "bundler",
47
+ "jsx": "react-jsx",
48
+ "jsxImportSource": "@emberkit/core",
49
+ "strict": true,
50
+ "esModuleInterop": true,
51
+ "skipLibCheck": true,
52
+ "forceConsistentCasingInFileNames": true,
53
+ "resolveJsonModule": true,
54
+ "isolatedModules": true,
55
+ "noEmit": true,
56
+ "lib": ["ES2022", "DOM", "DOM.Iterable"]${pathsEntry}
57
+ },
58
+ "include": ["src"],
59
+ "exclude": ["node_modules", "dist"]
60
+ }`;
61
+ }
62
+ export function buildViteConfig(hasTailwind = false) {
63
+ if (hasTailwind) {
64
+ return `import { defineConfig } from 'vite';
65
+ import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
66
+ import tailwindcss from '@tailwindcss/vite';
67
+
68
+ export default defineConfig({
69
+ plugins: [emberkitVitePlugin(), tailwindcss()],
70
+ server: {
71
+ port: 3000,
72
+ host: 'localhost',
73
+ },
74
+ esbuild: {
75
+ jsxImportSource: '@emberkit/core',
76
+ },
77
+ });`;
78
+ }
79
+ return `import { defineConfig } from 'vite';
80
+ import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
81
+
82
+ export default defineConfig({
83
+ plugins: [emberkitVitePlugin()],
84
+ server: {
85
+ port: 3000,
86
+ host: 'localhost',
87
+ },
88
+ esbuild: {
89
+ jsxImportSource: '@emberkit/core',
90
+ },
91
+ });`;
92
+ }
93
+ export function buildIndexHtml(options = {}) {
94
+ const { title = "{{name}}", fonts = [] } = options;
95
+ const fontLinks = fonts.length > 0
96
+ ? `\n <link rel="preconnect" href="https://fonts.googleapis.com">
97
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
98
+ ${fonts.map((href) => ` <link href="${href}" rel="stylesheet">`).join("\n")}`
99
+ : "";
100
+ return `<!DOCTYPE html>
101
+ <html lang="en">
102
+ <head>
103
+ <meta charset="UTF-8">
104
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
105
+ <title>${title}</title>${fontLinks}
106
+ </head>
107
+ <body id="app">
108
+ <script type="module" src="/src/index.tsx"></script>
109
+ </body>
110
+ </html>`;
111
+ }
112
+ export function buildEntryFile(options = {}) {
113
+ const { hasLayout = false, hasCss = false } = options;
114
+ const appImport = hasLayout
115
+ ? `import App from './routes/_layout';`
116
+ : `import App from './routes/index';`;
117
+ const cssImport = hasCss ? `\nimport './styles.css';` : "";
118
+ return `import { render } from '@emberkit/core';
119
+ import { routes } from 'virtual:emberkit-routes';
120
+ ${appImport}${cssImport}
121
+
122
+ const root = document.getElementById('app');
123
+
124
+ if (root) {
125
+ try {
126
+ render(App, root, { routes });
127
+ } catch (error) {
128
+ console.error('[entry] Render error:', error);
129
+ }
130
+ }`;
131
+ }
132
+ export const GITIGNORE = `node_modules/
133
+ dist/
134
+ .env
135
+ .env.local
136
+ *.local
137
+ .DS_Store
138
+ *.tsbuildinfo
139
+ `;