@emberkit/cli 0.6.0 → 0.6.1-alpha.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/dist/cli.js +44 -10
- package/dist/commands/create.js +10 -22
- package/dist/templates/action/index.js +12 -0
- package/dist/templates/apiRoute/index.js +20 -0
- package/dist/templates/component/index.js +14 -0
- package/dist/templates/config/index.js +13 -0
- package/dist/templates/context/index.js +21 -0
- package/dist/templates/entry/index.js +15 -0
- package/dist/templates/errorBoundary/index.js +17 -0
- package/dist/templates/form/index.js +59 -0
- package/dist/templates/layout/index.js +16 -0
- package/dist/templates/layoutRoutes/index.js +9 -0
- package/dist/templates/loader/index.js +10 -0
- package/dist/templates/project-templates/_shared/base.js +139 -0
- package/dist/templates/project-templates/api/api.js +209 -0
- package/dist/templates/project-templates/blog/blog.js +283 -0
- package/dist/templates/project-templates/dashboard/dashboard.js +331 -0
- package/dist/templates/project-templates/minimal/minimal.js +21 -0
- package/dist/templates/project-templates/saas/saas.js +461 -0
- package/dist/templates/project-templates/starter-kit/starter.js +209 -0
- package/dist/templates/project-templates/starter-kit/with-ui.js +375 -0
- package/dist/templates/route/index.js +12 -0
- package/dist/templates/signal/index.js +25 -0
- package/dist/utils/generator.js +13 -13
- package/package.json +1 -2
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,9 @@ import inquirer from "inquirer";
|
|
|
2
2
|
import { dev } from "./commands/dev.js";
|
|
3
3
|
import { build } from "./commands/build.js";
|
|
4
4
|
import { preview } from "./commands/preview.js";
|
|
5
|
-
import { create } from "./commands/create.js";
|
|
6
|
-
import {
|
|
5
|
+
import { create, TEMPLATES } from "./commands/create.js";
|
|
6
|
+
import { generate } from "./utils/generator.js";
|
|
7
|
+
import { toKebabCase } from "./templates/index.js";
|
|
7
8
|
export async function runCLI(args) {
|
|
8
9
|
const [command, ...restArgs] = args.slice(2);
|
|
9
10
|
if (!command) {
|
|
@@ -28,7 +29,7 @@ export async function runCLI(args) {
|
|
|
28
29
|
break;
|
|
29
30
|
case "--version":
|
|
30
31
|
case "-v":
|
|
31
|
-
console.log("EmberKit CLI v0.
|
|
32
|
+
console.log("EmberKit CLI v0.6.0");
|
|
32
33
|
break;
|
|
33
34
|
case "--help":
|
|
34
35
|
case "-h":
|
|
@@ -42,7 +43,7 @@ export async function runCLI(args) {
|
|
|
42
43
|
}
|
|
43
44
|
function showHelp() {
|
|
44
45
|
console.log(`
|
|
45
|
-
🔥 EmberKit CLI v0.
|
|
46
|
+
🔥 EmberKit CLI v0.6.0
|
|
46
47
|
|
|
47
48
|
Usage: emberkit <command> [options]
|
|
48
49
|
|
|
@@ -51,15 +52,24 @@ Commands:
|
|
|
51
52
|
dev Start development server
|
|
52
53
|
build Build for production
|
|
53
54
|
preview Preview production build
|
|
54
|
-
generate <type>
|
|
55
|
+
generate <type> <name> Generate a file from a template
|
|
55
56
|
|
|
56
57
|
Options:
|
|
57
58
|
--template, -t <id> Project template to use
|
|
58
59
|
--no-install Skip dependency installation
|
|
60
|
+
--path, -p <path> Output path for generate (overrides default)
|
|
59
61
|
--help, -h Show this help message
|
|
60
62
|
--version, -v Show version number
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
Generate types:
|
|
65
|
+
route Route component (src/routes/)
|
|
66
|
+
component UI component (src/components/)
|
|
67
|
+
layout Layout component (src/layouts/)
|
|
68
|
+
loader Data loader (src/loaders/)
|
|
69
|
+
action Form action (src/actions/)
|
|
70
|
+
api API route handler (src/routes/_api/)
|
|
71
|
+
|
|
72
|
+
Project templates:
|
|
63
73
|
basic Simple starter with Tailwind CSS (default)
|
|
64
74
|
with-ui Starter with EmberKit UI components
|
|
65
75
|
minimal Barebones project, no CSS framework
|
|
@@ -163,12 +173,36 @@ function extractFlagValue(args, longFlag, shortFlag) {
|
|
|
163
173
|
}
|
|
164
174
|
return undefined;
|
|
165
175
|
}
|
|
176
|
+
const GENERATE_TYPES = {
|
|
177
|
+
route: { dir: "src/routes", ext: ".tsx" },
|
|
178
|
+
component: { dir: "src/components", ext: ".tsx" },
|
|
179
|
+
layout: { dir: "src/layouts", ext: ".tsx" },
|
|
180
|
+
error: { dir: "src/routes", ext: ".tsx" },
|
|
181
|
+
loader: { dir: "src/loaders", ext: ".ts" },
|
|
182
|
+
action: { dir: "src/actions", ext: ".ts" },
|
|
183
|
+
api: { dir: "src/routes/_api", ext: ".ts" },
|
|
184
|
+
};
|
|
166
185
|
async function runGenerate(args) {
|
|
167
|
-
const
|
|
186
|
+
const nonFlagArgs = args.filter((a) => !a.startsWith("-"));
|
|
187
|
+
const [type, name] = nonFlagArgs;
|
|
168
188
|
if (!type || !name) {
|
|
169
|
-
console.error(
|
|
189
|
+
console.error(`Usage: emberkit generate <type> <name> [--path <file-path>]\n\nTypes: ${Object.keys(GENERATE_TYPES).join(", ")}`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
const explicitPath = extractFlagValue(args, "--path", "-p");
|
|
193
|
+
const typeConfig = GENERATE_TYPES[type];
|
|
194
|
+
if (!typeConfig && !explicitPath) {
|
|
195
|
+
console.error(`Unknown type "${type}". Valid types: ${Object.keys(GENERATE_TYPES).join(", ")}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
const filePath = explicitPath ??
|
|
199
|
+
`${typeConfig.dir}/${toKebabCase(name)}${typeConfig.ext}`;
|
|
200
|
+
const result = await generate({ name, path: filePath, template: type });
|
|
201
|
+
if (result.success) {
|
|
202
|
+
console.log(`✓ Generated ${type}: ${result.path}`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.error(`✗ ${result.error}`);
|
|
170
206
|
process.exit(1);
|
|
171
207
|
}
|
|
172
|
-
console.log(`🎨 Generating ${type}: ${name}`);
|
|
173
|
-
console.log("(Not yet implemented)");
|
|
174
208
|
}
|
package/dist/commands/create.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
-
import { resolve, join } from "path";
|
|
2
|
+
import { resolve, join, dirname } from "path";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
import { getPackageManager, getInstallCommand } from "../utils/filesystem.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
5
|
+
import { formatTemplate, toKebabCase } from "../templates/index.js";
|
|
6
|
+
import { starterFiles } from "../templates/project-templates/starter-kit/starter.js";
|
|
7
|
+
import { withUiTemplate } from "../templates/project-templates/starter-kit/with-ui.js";
|
|
8
|
+
import { minimalTemplate } from "../templates/project-templates/minimal/minimal.js";
|
|
9
|
+
import { blogTemplate } from "../templates/project-templates/blog/blog.js";
|
|
10
|
+
import { saasTemplate } from "../templates/project-templates/saas/saas.js";
|
|
11
|
+
import { dashboardTemplate } from "../templates/project-templates/dashboard/dashboard.js";
|
|
12
|
+
import { apiTemplate } from "../templates/project-templates/api/api.js";
|
|
12
13
|
const RESET = "\x1b[0m";
|
|
13
14
|
const BOLD = "\x1b[1m";
|
|
14
15
|
const DIM = "\x1b[2m";
|
|
@@ -68,19 +69,6 @@ function printInfo(message) {
|
|
|
68
69
|
const info = BRIGHT_BLUE + "›" + RESET;
|
|
69
70
|
console.log(` ${info} ${DIM + message + RESET}`);
|
|
70
71
|
}
|
|
71
|
-
function formatTemplate(template, vars) {
|
|
72
|
-
let result = template;
|
|
73
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
74
|
-
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
function toKebabCase(str) {
|
|
79
|
-
return str
|
|
80
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
81
|
-
.replace(/[\s_]+/g, "-")
|
|
82
|
-
.toLowerCase();
|
|
83
|
-
}
|
|
84
72
|
function getNpmPackageName(name) {
|
|
85
73
|
const kebab = toKebabCase(name);
|
|
86
74
|
return kebab.startsWith("@") ? kebab : kebab.replace(/^emberkit-/, "");
|
|
@@ -116,7 +104,7 @@ export async function create(options) {
|
|
|
116
104
|
const templateFiles = template.files;
|
|
117
105
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
118
106
|
const fullPath = join(targetDir, filePath);
|
|
119
|
-
const dir =
|
|
107
|
+
const dir = dirname(fullPath);
|
|
120
108
|
if (!existsSync(dir)) {
|
|
121
109
|
mkdirSync(dir, { recursive: true });
|
|
122
110
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const actionTemplate = `import type { ActionFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const action: ActionFunction = async ({ params, request }) => {
|
|
4
|
+
const formData = await request.formData();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
data: {
|
|
8
|
+
success: true,
|
|
9
|
+
},
|
|
10
|
+
} as LoaderResult<unknown>;
|
|
11
|
+
};
|
|
12
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const apiRouteTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const GET: LoaderFunction = async ({ params, query, request }) => {
|
|
4
|
+
return {
|
|
5
|
+
data: {
|
|
6
|
+
message: 'Hello from API',
|
|
7
|
+
},
|
|
8
|
+
} as LoaderResult<unknown>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const POST: LoaderFunction = async ({ request }) => {
|
|
12
|
+
const body = await request.json();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
data: {
|
|
16
|
+
received: body,
|
|
17
|
+
},
|
|
18
|
+
} as LoaderResult<unknown>;
|
|
19
|
+
};
|
|
20
|
+
`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const componentTemplate = `interface {{name}}Props {
|
|
2
|
+
className?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const {{name}} = ({ className = '' }: {{name}}Props) => {
|
|
6
|
+
return (
|
|
7
|
+
<div className={className}>
|
|
8
|
+
{{name}} component
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default {{name}};
|
|
14
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const contextTemplate = `import { createContext, useContext } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
interface {{name}}Context {
|
|
4
|
+
// Define your context shape
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const {{name}}Context = createContext<{{name}}Context>({
|
|
9
|
+
value: 'default',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Provider usage:
|
|
13
|
+
// <{{name}}Context.Provider value={{ value: 'hello' }}>
|
|
14
|
+
// {children}
|
|
15
|
+
// </{{name}}Context.Provider>
|
|
16
|
+
|
|
17
|
+
// Consumer usage:
|
|
18
|
+
// const ctx = useContext({{name}}Context);
|
|
19
|
+
|
|
20
|
+
export { {{name}}Context };
|
|
21
|
+
`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const indexTemplate = `import { render } from '@emberkit/core';
|
|
2
|
+
import { routes } from 'virtual:emberkit-routes';
|
|
3
|
+
import App from './routes/_layout';
|
|
4
|
+
import './styles.css';
|
|
5
|
+
|
|
6
|
+
const root = document.getElementById('app');
|
|
7
|
+
|
|
8
|
+
if (root) {
|
|
9
|
+
try {
|
|
10
|
+
render(App, root, { routes });
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error('[entry] Render error:', error);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const errorBoundaryTemplate = `import type { RouteComponent } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
interface {{name}}ErrorProps {
|
|
4
|
+
error: Error;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const {{name}}Error: RouteComponent<{{name}}ErrorProps> = ({ error }) => {
|
|
8
|
+
return (
|
|
9
|
+
<div className="error-boundary">
|
|
10
|
+
<h2>Something went wrong</h2>
|
|
11
|
+
<p>{error.message}</p>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default {{name}}Error;
|
|
17
|
+
`;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const formTemplate = `import { signal } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
const {{name}}Form = () => {
|
|
4
|
+
const email = signal('');
|
|
5
|
+
const password = signal('');
|
|
6
|
+
const error = signal<string | null>(null);
|
|
7
|
+
const loading = signal(false);
|
|
8
|
+
|
|
9
|
+
const handleSubmit = async (e: Event) => {
|
|
10
|
+
e.preventDefault();
|
|
11
|
+
error.value = null;
|
|
12
|
+
loading.value = true;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch('/api/auth/login', {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
email: email.value,
|
|
20
|
+
password: password.value,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error('Login failed');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Handle success
|
|
29
|
+
} catch (err) {
|
|
30
|
+
error.value = err instanceof Error ? err.message : 'Unknown error';
|
|
31
|
+
} finally {
|
|
32
|
+
loading.value = false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<form onSubmit={handleSubmit}>
|
|
38
|
+
<input
|
|
39
|
+
type="email"
|
|
40
|
+
value={email.value}
|
|
41
|
+
onInput={(e) => { email.value = e.currentTarget.value; }}
|
|
42
|
+
placeholder="Email"
|
|
43
|
+
/>
|
|
44
|
+
<input
|
|
45
|
+
type="password"
|
|
46
|
+
value={password.value}
|
|
47
|
+
onInput={(e) => { password.value = e.currentTarget.value; }}
|
|
48
|
+
placeholder="Password"
|
|
49
|
+
/>
|
|
50
|
+
{error.value && <p className="text-red-500">{error.value}</p>}
|
|
51
|
+
<button type="submit" disabled={loading.value}>
|
|
52
|
+
{loading.value ? 'Loading...' : 'Submit'}
|
|
53
|
+
</button>
|
|
54
|
+
</form>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default {{name}}Form;
|
|
59
|
+
`;
|
|
@@ -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,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,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
|
+
`;
|