create-elit 3.2.6 → 3.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/dist/index.js CHANGED
@@ -1,9 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { mkdir, writeFile } from "fs/promises";
5
- import { join, resolve } from "path";
4
+ import { mkdir, writeFile, readFile, readdir } from "fs/promises";
5
+ import { join, resolve, dirname } from "path";
6
6
  import { existsSync } from "fs";
7
+ import { fileURLToPath } from "url";
8
+ var __filename = fileURLToPath(import.meta.url);
9
+ var __dirname = dirname(__filename);
10
+ async function getElitVersion() {
11
+ const packageJsonPath = resolve(__dirname, "../package.json");
12
+ const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
13
+ return packageJson.version;
14
+ }
7
15
  var colors = {
8
16
  reset: "\x1B[0m",
9
17
  cyan: "\x1B[36m",
@@ -19,15 +27,40 @@ function getProjectName() {
19
27
  const args = process.argv.slice(2);
20
28
  return args[0] || "my-elit-app";
21
29
  }
30
+ async function copyDirectory(src, dest, replacements) {
31
+ await mkdir(dest, { recursive: true });
32
+ const entries = await readdir(src, { withFileTypes: true });
33
+ for (const entry of entries) {
34
+ const srcPath = join(src, entry.name);
35
+ const destPath = join(dest, entry.name);
36
+ if (entry.isDirectory()) {
37
+ await copyDirectory(srcPath, destPath, replacements);
38
+ } else {
39
+ await copyAndReplaceFile(srcPath, destPath, replacements);
40
+ }
41
+ }
42
+ }
43
+ async function copyAndReplaceFile(src, dest, replacements) {
44
+ let content = await readFile(src, "utf-8");
45
+ for (const [placeholder, value] of Object.entries(replacements)) {
46
+ content = content.split(placeholder).join(value);
47
+ }
48
+ await writeFile(dest, content, "utf-8");
49
+ }
22
50
  async function createProject(projectName2) {
23
51
  const projectPath = resolve(process.cwd(), projectName2);
52
+ const templatesPath = resolve(__dirname, "templates");
24
53
  if (existsSync(projectPath)) {
25
54
  log(`Error: Directory "${projectName2}" already exists!`, "red");
26
55
  process.exit(1);
27
56
  }
28
57
  log(`Creating a new Elit app in ${projectPath}...`, "cyan");
29
- await mkdir(projectPath, { recursive: true });
30
- await generateTemplate(projectPath, projectName2);
58
+ const elitVersion = await getElitVersion();
59
+ const replacements = {
60
+ "ELIT_PROJECT_NAME": projectName2,
61
+ "ELIT_VERSION": elitVersion
62
+ };
63
+ await copyDirectory(templatesPath, projectPath, replacements);
31
64
  log("\nSuccess! Created " + projectName2, "green");
32
65
  log("\nInside that directory, you can run several commands:", "dim");
33
66
  log("\n npm run dev", "cyan");
@@ -42,321 +75,6 @@ async function createProject(projectName2) {
42
75
  log(" npm run dev\n", "cyan");
43
76
  log("Happy coding! \u{1F680}", "green");
44
77
  }
45
- async function generateTemplate(projectPath, projectName2) {
46
- const packageJson = {
47
- name: projectName2,
48
- version: "0.0.0",
49
- type: "module",
50
- scripts: {
51
- dev: "elit dev",
52
- build: "elit build",
53
- preview: "elit preview"
54
- },
55
- dependencies: {
56
- elit: "^3.2.6"
57
- }
58
- };
59
- await writeFile(
60
- join(projectPath, "package.json"),
61
- JSON.stringify(packageJson, null, 2)
62
- );
63
- const tsConfig = {
64
- compilerOptions: {
65
- target: "ES2020",
66
- module: "ESNext",
67
- moduleResolution: "bundler",
68
- lib: ["ES2020", "DOM", "DOM.Iterable"],
69
- strict: true,
70
- esModuleInterop: true,
71
- skipLibCheck: true,
72
- resolveJsonModule: true,
73
- isolatedModules: true,
74
- types: ["node"]
75
- },
76
- include: ["src"]
77
- };
78
- await writeFile(
79
- join(projectPath, "tsconfig.json"),
80
- JSON.stringify(tsConfig, null, 2)
81
- );
82
- const gitignore = `node_modules
83
- dist
84
- .env
85
- .env.local
86
- *.log
87
- .DS_Store
88
- `;
89
- await writeFile(join(projectPath, ".gitignore"), gitignore);
90
- const readme = `# ${projectName2}
91
-
92
- A new Elit project created with create-elit.
93
-
94
- ## Getting Started
95
-
96
- \`\`\`bash
97
- npm install
98
- npm run dev
99
- \`\`\`
100
-
101
- Visit http://localhost:3000 to view your app.
102
-
103
- ## Available Scripts
104
-
105
- - \`npm run dev\` - Start development server with HMR
106
- - \`npm run build\` - Build for production
107
- - \`npm run preview\` - Preview production build
108
-
109
- ## Learn More
110
-
111
- - [Elit Documentation](https://d-osc.github.io/elit)
112
- - [GitHub Repository](https://github.com/d-osc/elit)
113
- `;
114
- await writeFile(join(projectPath, "README.md"), readme);
115
- const elitConfig = `import { server } from './src/server';
116
- import { client } from './src/client';
117
-
118
- export default {
119
- dev: {
120
- port: 3000,
121
- host: 'localhost',
122
- open: true,
123
- logging: true,
124
- clients: [{
125
- root: '.',
126
- basePath: '',
127
- ssr: () => client,
128
- api: server
129
- }]
130
- },
131
- build: [{
132
- entry: './src/main.ts',
133
- outDir: './dist',
134
- outFile: 'main.js',
135
- format: 'esm',
136
- minify: true,
137
- sourcemap: true,
138
- target: 'es2020',
139
- copy: [
140
- { from: './public/index.html', to: './index.html' }
141
- ]
142
- }],
143
- preview: {
144
- port: 3000,
145
- host: 'localhost',
146
- open: false,
147
- logging: true,
148
- root: './dist',
149
- basePath: '',
150
- index: './index.html'
151
- }
152
- };
153
- `;
154
- await writeFile(join(projectPath, "elit.config.ts"), elitConfig);
155
- await mkdir(join(projectPath, "public"), { recursive: true });
156
- const indexHtml = `<!DOCTYPE html>
157
- <html lang="en">
158
- <head>
159
- <meta charset="UTF-8">
160
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
161
- <title>${projectName2}</title>
162
- <link rel="icon" type="image/svg+xml" href="favicon.svg">
163
- <meta name="description" content="Built with Elit - Full-stack TypeScript framework">
164
- </head>
165
- <body>
166
- <div id="root"></div>
167
- <script type="module" src="/main.js"></script>
168
- </body>
169
- </html>
170
- `;
171
- await writeFile(join(projectPath, "public", "index.html"), indexHtml);
172
- await mkdir(join(projectPath, "src"), { recursive: true });
173
- const mainTs = `import { div, h1, h2, button, p } from 'elit/el';
174
- import { createState, reactive } from 'elit/state';
175
- import { render } from 'elit/dom';
176
- import './styles.ts';
177
-
178
- // Create reactive state
179
- export const count = createState(0);
180
-
181
- // Create app
182
- export const app = div({ className: 'container' },
183
- div({ className: 'card' },
184
- h1('Welcome to Elit! \u{1F680}'),
185
- p('A lightweight TypeScript framework with reactive state management'),
186
-
187
- div({ className: 'counter' },
188
- h2('Counter Example'),
189
- reactive(count, (value) =>
190
- div({ className: 'count-display' }, \`Count: \${value}\`)
191
- ),
192
- div({ className: 'button-group' },
193
- button({
194
- onclick: () => count.value--,
195
- className: 'btn btn-secondary'
196
- }, '- Decrement'),
197
- button({
198
- onclick: () => count.value = 0,
199
- className: 'btn btn-secondary'
200
- }, 'Reset'),
201
- button({
202
- onclick: () => count.value++,
203
- className: 'btn btn-primary'
204
- }, '+ Increment')
205
- )
206
- )
207
- )
208
- );
209
-
210
- render('root', app);
211
- console.log('[Main] App rendered');
212
- `;
213
- await writeFile(join(projectPath, "src", "main.ts"), mainTs);
214
- const stylesTs = `import styles from 'elit/style';
215
-
216
- // Global styles
217
- styles.addTag('*', {
218
- margin: 0,
219
- padding: 0,
220
- boxSizing: 'border-box'
221
- });
222
-
223
- styles.addTag('body', {
224
- fontFamily: 'system-ui, -apple-system, sans-serif',
225
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
226
- minHeight: '100vh',
227
- display: 'flex',
228
- alignItems: 'center',
229
- justifyContent: 'center',
230
- padding: '2rem'
231
- });
232
-
233
- // Container
234
- styles.addClass('container', {
235
- width: '100%',
236
- maxWidth: '600px'
237
- });
238
-
239
- // Card
240
- styles.addClass('card', {
241
- background: 'white',
242
- borderRadius: '16px',
243
- padding: '3rem',
244
- boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)'
245
- });
246
-
247
- // Typography
248
- styles.addTag('h1', {
249
- fontSize: '2.5rem',
250
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
251
- WebkitBackgroundClip: 'text',
252
- WebkitTextFillColor: 'transparent',
253
- backgroundClip: 'text',
254
- marginBottom: '1rem'
255
- });
256
-
257
- styles.addTag('h2', {
258
- fontSize: '1.5rem',
259
- color: '#333',
260
- marginBottom: '1rem'
261
- });
262
-
263
- styles.addTag('p', {
264
- color: '#666',
265
- marginBottom: '2rem',
266
- lineHeight: 1.6
267
- });
268
-
269
- // Counter section
270
- styles.addClass('counter', {
271
- marginTop: '2rem',
272
- paddingTop: '2rem',
273
- borderTop: '2px solid #f0f0f0'
274
- });
275
-
276
- styles.addClass('count-display', {
277
- fontSize: '3rem',
278
- fontWeight: 'bold',
279
- color: '#667eea',
280
- textAlign: 'center',
281
- margin: '2rem 0'
282
- });
283
-
284
- // Button group
285
- styles.addClass('button-group', {
286
- display: 'flex',
287
- gap: '1rem',
288
- justifyContent: 'center'
289
- });
290
-
291
- // Buttons
292
- styles.addClass('btn', {
293
- padding: '0.75rem 1.5rem',
294
- border: 'none',
295
- borderRadius: '8px',
296
- fontSize: '1rem',
297
- fontWeight: 600,
298
- cursor: 'pointer',
299
- transition: 'all 0.2s'
300
- });
301
-
302
- styles.addClass('btn-primary', {
303
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
304
- color: 'white'
305
- });
306
-
307
- styles.addPseudoClass('hover', {
308
- transform: 'translateY(-2px)',
309
- boxShadow: '0 4px 12px rgba(102, 126, 234, 0.4)'
310
- }, '.btn-primary');
311
-
312
- styles.addClass('btn-secondary', {
313
- background: '#f0f0f0',
314
- color: '#333'
315
- });
316
-
317
- styles.addPseudoClass('hover', {
318
- background: '#e0e0e0',
319
- transform: 'translateY(-2px)'
320
- }, '.btn-secondary');
321
-
322
- styles.addPseudoClass('active', {
323
- transform: 'translateY(0)'
324
- }, '.btn');
325
-
326
- styles.inject('global-styles');
327
- export default styles;
328
- `;
329
- await writeFile(join(projectPath, "src", "styles.ts"), stylesTs);
330
- const clientTs = `import { div, html, head, body, title, link, script, meta } from 'elit/el';
331
-
332
- export const client = html(
333
- head(
334
- title('${projectName2} - Elit App'),
335
- link({ rel: 'icon', type: 'image/svg+xml', href: 'favicon.svg' }),
336
- meta({ charset: 'UTF-8' }),
337
- meta({ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }),
338
- meta({ name: 'description', content: 'Elit - Full-stack TypeScript framework with dev server, HMR, routing, SSR, and REST API.' })
339
- ),
340
- body(
341
- div({ id: 'root' }),
342
- script({ type: 'module', src: '/src/main.js' })
343
- )
344
- );
345
- `;
346
- await writeFile(join(projectPath, "src", "client.ts"), clientTs);
347
- const serverTs = `import { ServerRouter } from 'elit/server';
348
-
349
- export const router = new ServerRouter();
350
-
351
- router.get('/api/hello', async (ctx) => {
352
- ctx.res.setHeader('Content-Type', 'application/json');
353
- ctx.res.end(JSON.stringify({ message: 'Hello from Elit ServerRouter!' }));
354
- });
355
-
356
- export const server = router;
357
- `;
358
- await writeFile(join(projectPath, "src", "server.ts"), serverTs);
359
- }
360
78
  var projectName = getProjectName();
361
79
  log("\n\u{1F680} Create Elit App\n", "cyan");
362
80
  createProject(projectName).catch((err) => {
@@ -0,0 +1,23 @@
1
+ # ELIT_PROJECT_NAME
2
+
3
+ A new Elit project created with create-elit.
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev
10
+ ```
11
+
12
+ Visit http://localhost:3003 to view your app.
13
+
14
+ ## Available Scripts
15
+
16
+ - `npm run dev` - Start development server with HMR
17
+ - `npm run build` - Build for production
18
+ - `npm run preview` - Preview production build
19
+
20
+ ## Learn More
21
+
22
+ - [Elit Documentation](https://d-osc.github.io/elit)
23
+ - [GitHub Repository](https://github.com/d-osc/elit)
@@ -0,0 +1,59 @@
1
+ import { server } from './src/server';
2
+ import { client } from './src/client';
3
+
4
+ export default {
5
+ dev: {
6
+ port: 3003,
7
+ host: 'localhost',
8
+ open: true,
9
+ logging: true,
10
+ clients: [{
11
+ root: '.',
12
+ basePath: '',
13
+ ssr: () => client,
14
+ api: server
15
+ }]
16
+ },
17
+ build: [{
18
+ entry: './src/main.ts',
19
+ outDir: './dist',
20
+ outFile: 'main.js',
21
+ format: 'esm',
22
+ minify: true,
23
+ sourcemap: true,
24
+ target: 'es2020',
25
+ copy: [
26
+ {
27
+ from: './public/index.html', to: './index.html',
28
+ transform: (content: string, config: { basePath: string; projectName: string; }) => {
29
+ // Replace script src
30
+ let html = content.replace('src="../src/main.ts"', 'src="main.js"');
31
+
32
+ // Replace project name placeholder
33
+ html = html.replace(/ELIT_PROJECT_NAME/g, config.projectName);
34
+
35
+ // Inject base tag if basePath is configured
36
+ if (config.basePath) {
37
+ const baseTag = `<base href="${config.basePath}/">`;
38
+ html = html.replace(
39
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
40
+ `<meta name="viewport" content="width=device-width, initial-scale=1.0">\n ${baseTag}`
41
+ );
42
+ }
43
+
44
+ return html;
45
+ }
46
+ },
47
+ { from: './public/favicon.svg', to: './favicon.svg' }
48
+ ]
49
+ }],
50
+ preview: {
51
+ port: 3000,
52
+ host: 'localhost',
53
+ open: false,
54
+ logging: true,
55
+ root: './dist',
56
+ basePath: '',
57
+ index: './index.html'
58
+ }
59
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "ELIT_PROJECT_NAME",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "elit dev",
7
+ "build": "elit build",
8
+ "preview": "elit preview"
9
+ },
10
+ "dependencies": {
11
+ "@types/node": "^25.0.9",
12
+ "elit": "^ELIT_VERSION"
13
+ }
14
+ }
@@ -0,0 +1,22 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
2
+ <defs>
3
+ <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#6366f1"/>
5
+ <stop offset="100%" stop-color="#8b5cf6"/>
6
+ </linearGradient>
7
+ </defs>
8
+
9
+ <!-- Clean background -->
10
+ <rect width="100" height="100" rx="20" fill="url(#grad)"/>
11
+
12
+ <!-- Simple E shape - 3 horizontal bars -->
13
+ <rect x="28" y="25" width="44" height="8" rx="4" fill="white"/>
14
+ <rect x="28" y="46" width="32" height="8" rx="4" fill="white"/>
15
+ <rect x="28" y="67" width="44" height="8" rx="4" fill="white"/>
16
+
17
+ <!-- Vertical connector -->
18
+ <rect x="28" y="25" width="8" height="50" rx="4" fill="white"/>
19
+
20
+ <!-- Single accent dot -->
21
+ <circle cx="72" cy="50" r="6" fill="white" opacity="0.5"/>
22
+ </svg>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ELIT_PROJECT_NAME</title>
7
+ <link rel="icon" type="image/svg+xml" href="favicon.svg">
8
+ <meta name="description" content="Built with Elit - Full-stack TypeScript framework">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ <script type="module" src="../src/main.ts"></script>
13
+ </body>
14
+ </html>
@@ -0,0 +1,15 @@
1
+ import { div, html, head, body, title, link, script, meta } from 'elit/el';
2
+
3
+ export const client = html(
4
+ head(
5
+ title('ELIT_PROJECT_NAME - Elit App'),
6
+ link({ rel: 'icon', type: 'image/svg+xml', href: 'public/favicon.svg' }),
7
+ meta({ charset: 'UTF-8' }),
8
+ meta({ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }),
9
+ meta({ name: 'description', content: 'Elit - Full-stack TypeScript framework with dev server, HMR, routing, SSR, and REST API.' })
10
+ ),
11
+ body(
12
+ div({ id: 'app' }),
13
+ script({ type: 'module', src: '/src/main.js' })
14
+ )
15
+ );
@@ -0,0 +1,20 @@
1
+ import { footer, div, p, a } from 'elit/el';
2
+
3
+ export function Footer() {
4
+ return footer({ className: 'footer' },
5
+ div({ className: 'footer-content' },
6
+ div({ className: 'footer-section' },
7
+ p({ className: 'footer-title' }, 'My Elit App'),
8
+ p({ className: 'footer-text' }, 'Built with Elit Framework')
9
+ ),
10
+ div({ className: 'footer-section' },
11
+ a({ href: 'https://github.com', target: '_blank', className: 'footer-link' }, 'GitHub'),
12
+ a({ href: '#', className: 'footer-link' }, 'Documentation'),
13
+ a({ href: '#', className: 'footer-link' }, 'Support')
14
+ ),
15
+ div({ className: 'footer-section' },
16
+ p({ className: 'footer-copyright' }, '© 2026 My Elit App. All rights reserved.')
17
+ )
18
+ )
19
+ );
20
+ }
@@ -0,0 +1,70 @@
1
+ import { header, nav, div, a, h1, button, span } from 'elit/el';
2
+ import { createState, reactive } from 'elit/state';
3
+ import type { Router } from 'elit';
4
+
5
+ export function Header(router: Router) {
6
+ // Check if user is logged in (has token in localStorage)
7
+ const isLoggedIn = createState(!!localStorage.getItem('token'));
8
+ const user = createState(() => {
9
+ const userStr = localStorage.getItem('user');
10
+ return userStr ? JSON.parse(userStr) : null;
11
+ });
12
+
13
+ // Listen for storage changes to update header when login/logout happens
14
+ const handleStorageChange = (e: StorageEvent) => {
15
+ if (e.key === 'token' || e.key === 'user') {
16
+ isLoggedIn.value = !!localStorage.getItem('token');
17
+ const userStr = localStorage.getItem('user');
18
+ user.value = userStr ? JSON.parse(userStr) : null;
19
+ }
20
+ };
21
+
22
+ // Also listen for custom storage events (same-tab updates)
23
+ const handleCustomStorageChange = () => {
24
+ isLoggedIn.value = !!localStorage.getItem('token');
25
+ const userStr = localStorage.getItem('user');
26
+ user.value = userStr ? JSON.parse(userStr) : null;
27
+ };
28
+
29
+ window.addEventListener('storage', handleStorageChange);
30
+ window.addEventListener('elit:storage', handleCustomStorageChange);
31
+
32
+ const handleLogout = () => {
33
+ localStorage.removeItem('token');
34
+ localStorage.removeItem('user');
35
+ isLoggedIn.value = false;
36
+ router.push('/');
37
+ };
38
+
39
+ return header({ className: 'header' },
40
+ nav({ className: 'nav' },
41
+ div({ className: 'nav-brand' },
42
+ a({ href: '#/', className: 'brand-link' },
43
+ h1({ className: 'brand-title' }, 'ELIT_PROJECT_NAME')
44
+ )
45
+ ),
46
+
47
+ reactive(isLoggedIn, (loggedIn) => {
48
+ if (loggedIn) {
49
+ return div({ className: 'nav-menu' },
50
+ a({ href: '#/chat/list', className: 'nav-link' }, 'Messages'),
51
+ a({ href: '#/profile', className: 'nav-link' }, 'Profile'),
52
+ reactive(user, (u) => u ? span({ className: 'nav-user' }, `Welcome, ${u.name}`) : null),
53
+ button({
54
+ className: 'btn btn-secondary btn-sm',
55
+ onclick: handleLogout
56
+ }, 'Logout')
57
+ );
58
+ }
59
+
60
+ return div({ className: 'nav-menu' },
61
+ a({ href: '#/login', className: 'nav-link' }, 'Login'),
62
+ button({
63
+ className: 'btn btn-primary btn-sm',
64
+ onclick: () => router.push('/register')
65
+ }, 'Sign Up')
66
+ );
67
+ })
68
+ )
69
+ );
70
+ }
@@ -0,0 +1,2 @@
1
+ export { Header } from './Header';
2
+ export { Footer } from './Footer';
@@ -0,0 +1,22 @@
1
+ import { div, main } from 'elit/el';
2
+ import { reactive } from 'elit/state';
3
+ import { dom } from 'elit/dom';
4
+ import { injectStyles } from './styles';
5
+ import { router, RouterView } from './router';
6
+ import { Header } from './components/Header';
7
+ import { Footer } from './components/Footer';
8
+
9
+ injectStyles()
10
+ // Create reactive state (shared between SSR and client)
11
+ // Main App
12
+ const App = () =>
13
+ div(
14
+ Header(router),
15
+ main(
16
+ reactive(router.currentRoute, () => RouterView())
17
+ ),
18
+ Footer()
19
+ );
20
+
21
+ // Render
22
+ dom.render('#app', App());