ether-code 0.2.0 → 0.2.4

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/cli/ether.js CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
+ const http = require('http')
5
6
  const { EtherCompiler } = require('./compiler')
6
7
  const { Watcher } = require('./watcher')
7
8
 
8
- const VERSION = '0.2.0'
9
+ const VERSION = '0.2.4'
9
10
 
10
11
  const COLORS = {
11
12
  reset: '\x1b[0m',
@@ -62,7 +63,7 @@ ${COLORS.bright}UTILISATION${COLORS.reset}
62
63
  ${COLORS.bright}COMMANDES${COLORS.reset}
63
64
  ${COLORS.cyan}init${COLORS.reset} Initialiser un nouveau projet Ether
64
65
  ${COLORS.cyan}build${COLORS.reset} Compiler les fichiers .eth
65
- ${COLORS.cyan}dev${COLORS.reset} Mode développement (watch + compilation auto)
66
+ ${COLORS.cyan}dev${COLORS.reset} Mode développement (watch + serveur + live-reload)
66
67
  ${COLORS.cyan}check${COLORS.reset} Vérifier la syntaxe sans compiler
67
68
  ${COLORS.cyan}help${COLORS.reset} Afficher cette aide
68
69
  ${COLORS.cyan}version${COLORS.reset} Afficher la version
@@ -70,10 +71,12 @@ ${COLORS.bright}COMMANDES${COLORS.reset}
70
71
  ${COLORS.bright}OPTIONS${COLORS.reset}
71
72
  -c, --config Chemin vers le fichier de configuration
72
73
  -o, --output Dossier de sortie
74
+ -p, --port Port du serveur (défaut: 3000)
73
75
  -w, --watch Surveiller les changements (alias de dev)
74
76
  -v, --verbose Mode verbeux
75
77
  -q, --quiet Mode silencieux
76
78
  --no-color Désactiver les couleurs
79
+ --no-server Désactiver le serveur en mode dev
77
80
 
78
81
  ${COLORS.bright}EXEMPLES${COLORS.reset}
79
82
  ${COLORS.dim}# Initialiser un projet${COLORS.reset}
@@ -82,11 +85,11 @@ ${COLORS.bright}EXEMPLES${COLORS.reset}
82
85
  ${COLORS.dim}# Compiler le projet${COLORS.reset}
83
86
  ether build
84
87
 
85
- ${COLORS.dim}# Mode développement${COLORS.reset}
88
+ ${COLORS.dim}# Mode développement avec serveur${COLORS.reset}
86
89
  ether dev
87
90
 
88
- ${COLORS.dim}# Compiler avec config personnalisée${COLORS.reset}
89
- ether build -c ./mon-config.js
91
+ ${COLORS.dim}# Mode développement sur port 8080${COLORS.reset}
92
+ ether dev -p 8080
90
93
 
91
94
  ${COLORS.bright}DOCUMENTATION${COLORS.reset}
92
95
  https://ether-code.com/docs
@@ -103,6 +106,7 @@ function loadConfig(configPath) {
103
106
  out: './dist',
104
107
  targets: {},
105
108
  i18n: 'fr',
109
+ port: 3000,
106
110
  watch: {
107
111
  ignored: ['node_modules', '.git', 'dist']
108
112
  }
@@ -135,6 +139,142 @@ function loadConfig(configPath) {
135
139
  return defaultConfig
136
140
  }
137
141
 
142
+ const MIME_TYPES = {
143
+ '.html': 'text/html',
144
+ '.css': 'text/css',
145
+ '.js': 'application/javascript',
146
+ '.json': 'application/json',
147
+ '.png': 'image/png',
148
+ '.jpg': 'image/jpeg',
149
+ '.jpeg': 'image/jpeg',
150
+ '.gif': 'image/gif',
151
+ '.svg': 'image/svg+xml',
152
+ '.ico': 'image/x-icon',
153
+ '.woff': 'font/woff',
154
+ '.woff2': 'font/woff2',
155
+ '.ttf': 'font/ttf',
156
+ '.eot': 'application/vnd.ms-fontobject'
157
+ }
158
+
159
+ const LIVE_RELOAD_SCRIPT = `
160
+ <script>
161
+ (function() {
162
+ var source = new EventSource('/__ether_live_reload');
163
+ source.onmessage = function(e) {
164
+ if (e.data === 'reload') {
165
+ window.location.reload();
166
+ }
167
+ };
168
+ source.onerror = function() {
169
+ console.log('[Ether] Connexion au serveur perdue, tentative de reconnexion...');
170
+ };
171
+ })();
172
+ </script>
173
+ `
174
+
175
+ function createDevServer(outDir, port) {
176
+ const clients = []
177
+
178
+ const server = http.createServer((req, res) => {
179
+ if (req.url === '/__ether_live_reload') {
180
+ res.writeHead(200, {
181
+ 'Content-Type': 'text/event-stream',
182
+ 'Cache-Control': 'no-cache',
183
+ 'Connection': 'keep-alive',
184
+ 'Access-Control-Allow-Origin': '*'
185
+ })
186
+ res.write('data: connected\n\n')
187
+ clients.push(res)
188
+ req.on('close', () => {
189
+ const index = clients.indexOf(res)
190
+ if (index !== -1) clients.splice(index, 1)
191
+ })
192
+ return
193
+ }
194
+
195
+ let filePath = path.join(outDir, req.url === '/' ? 'index.html' : req.url)
196
+
197
+ if (!filePath.includes('.') && !filePath.endsWith('/')) {
198
+ const htmlPath = filePath + '.html'
199
+ if (fs.existsSync(htmlPath)) {
200
+ filePath = htmlPath
201
+ }
202
+ }
203
+
204
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
205
+ filePath = path.join(filePath, 'index.html')
206
+ }
207
+
208
+ if (!fs.existsSync(filePath)) {
209
+ res.writeHead(404, { 'Content-Type': 'text/html' })
210
+ res.end(`
211
+ <!DOCTYPE html>
212
+ <html>
213
+ <head><title>404 - Non trouvé</title></head>
214
+ <body style="font-family: system-ui; padding: 40px; text-align: center;">
215
+ <h1>404</h1>
216
+ <p>Fichier non trouvé: ${req.url}</p>
217
+ <p style="color: #666;">Serveur Ether Dev</p>
218
+ </body>
219
+ </html>
220
+ `)
221
+ return
222
+ }
223
+
224
+ const ext = path.extname(filePath).toLowerCase()
225
+ const mimeType = MIME_TYPES[ext] || 'application/octet-stream'
226
+
227
+ fs.readFile(filePath, (err, content) => {
228
+ if (err) {
229
+ res.writeHead(500)
230
+ res.end(`Erreur serveur: ${err.message}`)
231
+ return
232
+ }
233
+
234
+ if (ext === '.html') {
235
+ let html = content.toString()
236
+ if (html.includes('</body>')) {
237
+ html = html.replace('</body>', `${LIVE_RELOAD_SCRIPT}</body>`)
238
+ } else if (html.includes('</html>')) {
239
+ html = html.replace('</html>', `${LIVE_RELOAD_SCRIPT}</html>`)
240
+ } else {
241
+ html += LIVE_RELOAD_SCRIPT
242
+ }
243
+ content = html
244
+ }
245
+
246
+ res.writeHead(200, { 'Content-Type': mimeType })
247
+ res.end(content)
248
+ })
249
+ })
250
+
251
+ server.listen(port, () => {
252
+ logSuccess(`Serveur démarré sur ${COLORS.cyan}http://localhost:${port}${COLORS.reset}`)
253
+ })
254
+
255
+ server.on('error', (err) => {
256
+ if (err.code === 'EADDRINUSE') {
257
+ logError(`Le port ${port} est déjà utilisé. Essayez: ether dev -p ${port + 1}`)
258
+ } else {
259
+ logError(`Erreur serveur: ${err.message}`)
260
+ }
261
+ process.exit(1)
262
+ })
263
+
264
+ return {
265
+ server,
266
+ reload: () => {
267
+ clients.forEach(client => {
268
+ client.write('data: reload\n\n')
269
+ })
270
+ },
271
+ close: () => {
272
+ clients.forEach(client => client.end())
273
+ server.close()
274
+ }
275
+ }
276
+ }
277
+
138
278
  async function cmdInit() {
139
279
  showBanner()
140
280
  logInfo('Initialisation du projet Ether...')
@@ -153,11 +293,50 @@ async function cmdInit() {
153
293
  logSuccess('Dossier dist/ créé')
154
294
  }
155
295
 
296
+ const publicDir = path.join(cwd, 'public')
297
+ if (!fs.existsSync(publicDir)) {
298
+ fs.mkdirSync(publicDir, { recursive: true })
299
+ logSuccess('Dossier public/ créé')
300
+ }
301
+
302
+ const imagesDir = path.join(publicDir, 'images')
303
+ if (!fs.existsSync(imagesDir)) {
304
+ fs.mkdirSync(imagesDir, { recursive: true })
305
+ logSuccess('Dossier public/images/ créé')
306
+ }
307
+
308
+ const videosDir = path.join(publicDir, 'videos')
309
+ if (!fs.existsSync(videosDir)) {
310
+ fs.mkdirSync(videosDir, { recursive: true })
311
+ logSuccess('Dossier public/videos/ créé')
312
+ }
313
+
314
+ const projectName = path.basename(cwd)
315
+ const packageJsonPath = path.join(cwd, 'package.json')
316
+ if (!fs.existsSync(packageJsonPath)) {
317
+ const packageJson = {
318
+ name: projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
319
+ version: '1.0.0',
320
+ description: 'Projet Ether',
321
+ main: 'dist/index.html',
322
+ scripts: {
323
+ dev: 'ether dev',
324
+ build: 'ether build'
325
+ },
326
+ keywords: ['ether'],
327
+ author: '',
328
+ license: 'MIT'
329
+ }
330
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
331
+ logSuccess('Fichier package.json créé')
332
+ }
333
+
156
334
  const configContent = `module.exports = {
157
335
  src: './src',
158
336
  out: './dist',
159
337
 
160
338
  i18n: 'fr',
339
+ port: 3000,
161
340
 
162
341
  targets: {
163
342
  css: {
@@ -188,38 +367,387 @@ async function cmdInit() {
188
367
  logWarning('ether.config.js existe déjà')
189
368
  }
190
369
 
191
- const exampleEth = `// cible: html
370
+ const exampleHtml = `// cible: html
192
371
 
193
372
  document
194
373
  tete
195
- titre "Ma première page Ether"
374
+ meta charset: "UTF-8"
375
+ meta name: "viewport", content: "width=device-width, initial-scale=1.0"
376
+ titre "Ether - Le langage intentionnel"
377
+ lien rel: "stylesheet", href: "styles.css"
196
378
 
197
379
  corps
198
- entete
199
- titre1 "Bienvenue sur Ether"
380
+ entete classe: "hero"
381
+ nav classe: "navbar"
382
+ div classe: "logo"
383
+ span classe: "logo-icon"
384
+ "◈"
385
+ span "Ether"
386
+ liste-non-ordonnee classe: "nav-links"
387
+ element-liste
388
+ lien href: "#features"
389
+ "Fonctionnalités"
390
+ element-liste
391
+ lien href: "#examples"
392
+ "Exemples"
393
+ element-liste
394
+ lien href: "#docs"
395
+ "Documentation"
396
+
397
+ div classe: "hero-content"
398
+ titre1 classe: "hero-title"
399
+ "Écrivez du code"
400
+ saut-ligne
401
+ span classe: "gradient-text"
402
+ "comme vous pensez"
403
+ paragraphe classe: "hero-subtitle"
404
+ "Ether est un langage intentionnel qui compile vers HTML, CSS, JavaScript et plus encore. Naturel, intuitif, puissant."
405
+ div classe: "hero-buttons"
406
+ lien href: "#start", classe: "btn btn-primary"
407
+ "Commencer"
408
+ lien href: "#learn", classe: "btn btn-secondary"
409
+ "En savoir plus"
200
410
 
201
411
  principal
202
- section
203
- titre2 "Le langage intentionnel"
204
- paragraphe "Ether permet d'écrire du code comme on pense."
205
-
206
- pied
207
- paragraphe "Créé avec Ether"
412
+ section id: "features", classe: "features"
413
+ titre2 classe: "section-title"
414
+ "Pourquoi Ether ?"
415
+ div classe: "features-grid"
416
+ article classe: "feature-card"
417
+ div classe: "feature-icon"
418
+ "🎯"
419
+ titre3 "Intuitif"
420
+ paragraphe "Écrivez du code en langage naturel. Plus besoin de mémoriser des syntaxes complexes."
421
+ article classe: "feature-card"
422
+ div classe: "feature-icon"
423
+ "⚡"
424
+ titre3 "Rapide"
425
+ paragraphe "Compilation instantanée avec rechargement automatique en mode développement."
426
+ article classe: "feature-card"
427
+ div classe: "feature-icon"
428
+ "🌍"
429
+ titre3 "Multilingue"
430
+ paragraphe "Codez en français, anglais, espagnol et plus. Votre langue, votre code."
431
+ article classe: "feature-card"
432
+ div classe: "feature-icon"
433
+ "🔧"
434
+ titre3 "Flexible"
435
+ paragraphe "Compile vers HTML, CSS, JS, PHP, Python, SQL et bien d'autres langages."
436
+
437
+ section id: "examples", classe: "examples"
438
+ titre2 classe: "section-title"
439
+ "Simple et élégant"
440
+ div classe: "code-comparison"
441
+ div classe: "code-block"
442
+ div classe: "code-header"
443
+ "Ether"
444
+ pre
445
+ code
446
+ "document\\n corps\\n titre1 \\"Bonjour\\"\\n paragraphe \\"Le monde\\""
447
+ div classe: "code-arrow"
448
+ "→"
449
+ div classe: "code-block"
450
+ div classe: "code-header"
451
+ "HTML"
452
+ pre
453
+ code
454
+ "&lt;!DOCTYPE html&gt;\\n&lt;html&gt;\\n &lt;body&gt;\\n &lt;h1&gt;Bonjour&lt;/h1&gt;\\n &lt;p&gt;Le monde&lt;/p&gt;\\n &lt;/body&gt;\\n&lt;/html&gt;"
455
+
456
+ pied classe: "footer"
457
+ paragraphe
458
+ "Créé avec"
459
+ span classe: "heart"
460
+ " ♥ "
461
+ "par la communauté Ether"
462
+ paragraphe classe: "copyright"
463
+ "© 2025 Ether. MIT License."
464
+
465
+ script src: "app.js"
466
+ `
467
+
468
+ const exampleCss = `// cible: css
469
+
470
+ racine
471
+ couleur-primaire: "#6366f1"
472
+ couleur-secondaire: "#8b5cf6"
473
+ couleur-accent: "#06b6d4"
474
+ couleur-fond: "#0f0f23"
475
+ couleur-surface: "#1a1a2e"
476
+ couleur-texte: "#e2e8f0"
477
+ couleur-texte-pale: "#94a3b8"
478
+
479
+ *
480
+ marge: 0
481
+ remplissage: 0
482
+ modele-boite: border-box
483
+
484
+ corps
485
+ famille-police: "system-ui", "-apple-system", "sans-serif"
486
+ couleur-fond: var(--couleur-fond)
487
+ couleur: var(--couleur-texte)
488
+ hauteur-ligne: 1.6
489
+
490
+ .hero
491
+ min-hauteur: 100vh
492
+ fond: "linear-gradient(135deg, var(--couleur-fond) 0%, var(--couleur-surface) 100%)"
493
+ position: relative
494
+ debordement: cache
495
+
496
+ .navbar
497
+ affichage: flex
498
+ justifier-contenu: space-between
499
+ aligner-elements: center
500
+ remplissage: "1.5rem 5%"
501
+ position: fixed
502
+ largeur: 100%
503
+ haut: 0
504
+ z-index: 100
505
+ fond: "rgba(15, 15, 35, 0.9)"
506
+ flou-fond: "10px"
507
+
508
+ .logo
509
+ affichage: flex
510
+ aligner-elements: center
511
+ espace: "0.5rem"
512
+ taille-police: "1.5rem"
513
+ poids-police: 700
514
+
515
+ .logo-icon
516
+ taille-police: "2rem"
517
+ couleur: var(--couleur-primaire)
518
+
519
+ .nav-links
520
+ affichage: flex
521
+ liste-style: none
522
+ espace: "2rem"
523
+
524
+ .nav-links lien
525
+ couleur: var(--couleur-texte-pale)
526
+ decoration-texte: none
527
+ transition: "couleur 0.3s"
528
+ au survol
529
+ couleur: var(--couleur-primaire)
530
+
531
+ .hero-content
532
+ texte-aligne: center
533
+ remplissage: "12rem 5% 5rem"
534
+ max-largeur: "800px"
535
+ marge: "0 auto"
536
+
537
+ .hero-title
538
+ taille-police: "3.5rem"
539
+ poids-police: 800
540
+ marge-bas: "1.5rem"
541
+ hauteur-ligne: 1.2
542
+
543
+ .gradient-text
544
+ fond: "linear-gradient(90deg, var(--couleur-primaire), var(--couleur-accent))"
545
+ decoupe-fond: text
546
+ couleur: transparent
547
+
548
+ .hero-subtitle
549
+ taille-police: "1.25rem"
550
+ couleur: var(--couleur-texte-pale)
551
+ marge-bas: "2.5rem"
552
+ max-largeur: "600px"
553
+ marge-gauche: auto
554
+ marge-droite: auto
555
+
556
+ .hero-buttons
557
+ affichage: flex
558
+ espace: "1rem"
559
+ justifier-contenu: center
560
+
561
+ .btn
562
+ remplissage: "0.875rem 2rem"
563
+ rayon-bordure: "0.5rem"
564
+ taille-police: "1rem"
565
+ poids-police: 600
566
+ decoration-texte: none
567
+ transition: "transform 0.2s, box-shadow 0.2s"
568
+ au survol
569
+ transformation: "translateY(-2px)"
570
+
571
+ .btn-primary
572
+ fond: "linear-gradient(90deg, var(--couleur-primaire), var(--couleur-secondaire))"
573
+ couleur: white
574
+ au survol
575
+ ombre-boite: "0 10px 30px rgba(99, 102, 241, 0.4)"
576
+
577
+ .btn-secondary
578
+ fond: transparent
579
+ couleur: var(--couleur-texte)
580
+ bordure: "2px solid var(--couleur-primaire)"
581
+ au survol
582
+ fond: "rgba(99, 102, 241, 0.1)"
583
+
584
+ .features
585
+ remplissage: "6rem 5%"
586
+
587
+ .section-title
588
+ texte-aligne: center
589
+ taille-police: "2.5rem"
590
+ marge-bas: "3rem"
591
+
592
+ .features-grid
593
+ affichage: grid
594
+ colonnes-grille: "repeat(auto-fit, minmax(250px, 1fr))"
595
+ espace: "2rem"
596
+ max-largeur: "1200px"
597
+ marge: "0 auto"
598
+
599
+ .feature-card
600
+ fond: var(--couleur-surface)
601
+ remplissage: "2rem"
602
+ rayon-bordure: "1rem"
603
+ texte-aligne: center
604
+ transition: "transform 0.3s"
605
+ au survol
606
+ transformation: "translateY(-5px)"
607
+
608
+ .feature-icon
609
+ taille-police: "3rem"
610
+ marge-bas: "1rem"
611
+
612
+ .feature-card titre3
613
+ marge-bas: "0.75rem"
614
+ taille-police: "1.25rem"
615
+
616
+ .feature-card paragraphe
617
+ couleur: var(--couleur-texte-pale)
618
+
619
+ .examples
620
+ remplissage: "6rem 5%"
621
+ fond: var(--couleur-surface)
622
+
623
+ .code-comparison
624
+ affichage: flex
625
+ aligner-elements: center
626
+ justifier-contenu: center
627
+ espace: "2rem"
628
+ flex-wrap: wrap
629
+ max-largeur: "1000px"
630
+ marge: "0 auto"
631
+
632
+ .code-block
633
+ fond: var(--couleur-fond)
634
+ rayon-bordure: "0.75rem"
635
+ debordement: cache
636
+ min-largeur: "300px"
637
+
638
+ .code-header
639
+ fond: "rgba(99, 102, 241, 0.2)"
640
+ remplissage: "0.75rem 1rem"
641
+ poids-police: 600
642
+ couleur: var(--couleur-primaire)
643
+
644
+ .code-block pre
645
+ remplissage: "1.5rem"
646
+ marge: 0
647
+
648
+ .code-block code
649
+ famille-police: "Fira Code", "Monaco", monospace
650
+ taille-police: "0.9rem"
651
+ blanc: pre
652
+
653
+ .code-arrow
654
+ taille-police: "2rem"
655
+ couleur: var(--couleur-primaire)
656
+
657
+ .footer
658
+ texte-aligne: center
659
+ remplissage: "3rem 5%"
660
+ fond: var(--couleur-fond)
661
+
662
+ .heart
663
+ couleur: "#ef4444"
664
+
665
+ .copyright
666
+ marge-haut: "0.5rem"
667
+ couleur: var(--couleur-texte-pale)
668
+ taille-police: "0.875rem"
669
+
670
+ @media (max-largeur: 768px)
671
+ .hero-title
672
+ taille-police: "2.5rem"
673
+ .nav-links
674
+ affichage: none
675
+ .code-arrow
676
+ transformation: "rotate(90deg)"
677
+ `
678
+
679
+ const exampleJs = `// cible: js
680
+
681
+ constante annee = nouveau Date().getFullYear()
682
+
683
+ fonction initialiser()
684
+ constante copyright = document.querySelector(".copyright")
685
+ si copyright
686
+ copyright.textContent = "© " + annee + " Ether. MIT License."
687
+
688
+ constante liens = document.querySelectorAll("a[href^='#']")
689
+ pour chaque lien dans liens
690
+ lien.addEventListener "click", fonction(e)
691
+ e.preventDefault()
692
+ constante cible = document.querySelector(this.getAttribute("href"))
693
+ si cible
694
+ cible.scrollIntoView({ behavior: "smooth" })
695
+
696
+ document.addEventListener "DOMContentLoaded", initialiser
208
697
  `
209
698
 
210
699
  const examplePath = path.join(srcDir, 'index.eth')
211
700
  if (!fs.existsSync(examplePath)) {
212
- fs.writeFileSync(examplePath, exampleEth)
213
- logSuccess('Fichier exemple src/index.eth créé')
701
+ fs.writeFileSync(examplePath, exampleHtml)
702
+ logSuccess('Fichier src/index.eth créé')
703
+ }
704
+
705
+ const cssPath = path.join(srcDir, 'styles.eth')
706
+ if (!fs.existsSync(cssPath)) {
707
+ fs.writeFileSync(cssPath, exampleCss)
708
+ logSuccess('Fichier src/styles.eth créé')
709
+ }
710
+
711
+ const jsPath = path.join(srcDir, 'app.eth')
712
+ if (!fs.existsSync(jsPath)) {
713
+ fs.writeFileSync(jsPath, exampleJs)
714
+ logSuccess('Fichier src/app.eth créé')
715
+ }
716
+
717
+ const logoSvg = `<?xml version="1.0" encoding="UTF-8"?>
718
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 499 500">
719
+ <defs>
720
+ <linearGradient id="g1" gradientUnits="userSpaceOnUse" x1="1.106" y1="250.0527" x2="498.8623" y2="250.0527">
721
+ <stop offset="0" style="stop-color:#03AFC6"/><stop offset="0.5126" style="stop-color:#31BAD3"/><stop offset="1" style="stop-color:#3EC4DD"/>
722
+ </linearGradient>
723
+ <linearGradient id="g2" gradientUnits="userSpaceOnUse" x1="56.332" y1="250.0532" x2="470.3037" y2="250.0532">
724
+ <stop offset="0" style="stop-color:#4758A7"/><stop offset="0.2597" style="stop-color:#6F53A2"/><stop offset="0.5939" style="stop-color:#924A9D"/><stop offset="0.8518" style="stop-color:#A64399"/><stop offset="1" style="stop-color:#AE4097"/>
725
+ </linearGradient>
726
+ <linearGradient id="g3" gradientUnits="userSpaceOnUse" x1="144.1313" y1="289.1055" x2="448.2786" y2="147.2793">
727
+ <stop offset="0" style="stop-color:#5E6AB2"/><stop offset="0.1138" style="stop-color:#5A77B7"/><stop offset="0.4161" style="stop-color:#4D96C8"/><stop offset="0.6753" style="stop-color:#3DADD5"/><stop offset="0.8782" style="stop-color:#30BBDD"/><stop offset="1" style="stop-color:#24C1E1"/>
728
+ </linearGradient>
729
+ <linearGradient id="g4" gradientUnits="userSpaceOnUse" x1="223.2749" y1="258.8877" x2="387.1369" y2="182.4776">
730
+ <stop offset="0" style="stop-color:#5E6AB2"/><stop offset="0.1138" style="stop-color:#5A77B7"/><stop offset="0.4161" style="stop-color:#4D96C8"/><stop offset="0.6753" style="stop-color:#3DADD5"/><stop offset="0.8782" style="stop-color:#30BBDD"/><stop offset="1" style="stop-color:#24C1E1"/>
731
+ </linearGradient>
732
+ </defs>
733
+ <path fill="url(#g1)" d="M5.381,238.563c-5.468-13.48-5.838-18.365-0.256-29.006c5.582-10.641,129.113-178.821,157.852-196.679C191.716-4.979,414.415,0.938,431.034,1.795c16.621,0.856,39.463,3.869,45.858,10.234c6.395,6.365,10.242,8.502,12.175,17.908c1.932,9.406,7.304,45.665,8.664,91.185c1.362,45.521,1.822,152.92-0.505,186.578c-2.325,33.658-1.465,47.184-14.553,67.173c-13.089,19.987-218.044,111.611-232.689,117.413c-14.646,5.801-35.404,16.623-62.205-6.569C160.979,462.526,5.381,238.563,5.381,238.563z"/>
734
+ <path fill="url(#g2)" d="M291.136,436.653c-7.71,3.354-52.39,24.228-61.917,23.915c-10.871-0.359-16.045-4.679-36.578-31.196C157.484,383.969,67.434,258.436,60.702,243.983c-5.766-12.377-6.472-19.474,1.826-32.931c8.299-13.456,105.229-151.605,117.666-160.906c12.648-9.458,23.041-10.032,35.5-10.188c55.329-0.694,152.766-0.725,225.992,1.368c16.294,0.466,24.063,11.799,24.653,21.583c5.707,94.576,6.6,150.435-6.481,266.374c-3.107,27.537-16.15,39.634-36.553,49.895C402.604,389.586,301.741,432.036,291.136,436.653z"/>
735
+ <path fill="url(#g3)" d="M397.598,278.148c2.596,0.083,3.073,1.179,3.474,1.965c0.642,1.265,0.51,3.642-0.264,5.969c-0.545,1.638-10.225,33.388-13.646,43.11c-1.364,3.871-2.896,4.9-5.702,6.357c-2.692,1.396-132.598,52.559-143.222,56.099c-6.013,2.006-6.541,0.742-9.292-1.695c-2.749-2.437-107.08-151.266-109.972-155.301c-2.893-4.034-1.419-6.227-0.711-7.854c1.636-3.759,78.848-110.176,84.417-116.574c5.57-6.398,8.097-8.37,12.46-11.03c2.771-1.689,5.406-3.374,11.53-3.739c12.276-0.731,183.793-0.426,192.787,0.333c4.049,0.342,7.233,1.441,6.384,6.319c-0.853,4.879-9.874,46.584-8.671,43.199c1.204-3.385-1.01,5.33-8.432,5.348c-7.422,0.02-153.731,0-157.729,0.039c-6.107,0.06-6.991,0.443-7.952,1.426c-2.176,2.226-54.269,71.788-57.427,75.836c-3.157,4.048-2.247,5.316,1.448,9.065c3.695,3.748,69.606,62.824,69.606,62.824s-10.604-21.547-8.459-21.547"/>
736
+ <path fill="url(#g4)" d="M213.282,235.909c14.12-20.019,28.129-36.342,31.945-40.798c5.362-6.26,5.762-5.197,8.629-5.197c2.869,0,132.714,0.003,132.714,0.003s2.916-0.469,4.863,1.864c1.029,1.235-0.298,5.654-0.298,5.654l-9.552,40.332c0,0-1.318,5.72-6.366,5.649C334.696,242.847,208.748,250.903,213.282,235.909"/>
737
+ </svg>`
738
+
739
+ const logoPath = path.join(imagesDir, 'Logo_Ether.svg')
740
+ if (!fs.existsSync(logoPath)) {
741
+ fs.writeFileSync(logoPath, logoSvg)
742
+ logSuccess('Fichier public/images/Logo_Ether.svg créé')
214
743
  }
215
744
 
216
745
  console.log('')
217
746
  logSuccess('Projet initialisé avec succès!')
218
747
  console.log('')
219
748
  logInfo('Prochaines étapes:')
220
- console.log(` 1. Éditer ${COLORS.cyan}src/index.eth${COLORS.reset}`)
221
- console.log(` 2. Lancer ${COLORS.cyan}ether dev${COLORS.reset} pour le mode développement`)
222
- console.log(` 3. Ou ${COLORS.cyan}ether build${COLORS.reset} pour compiler`)
749
+ console.log(` 1. Lancer ${COLORS.cyan}ether dev${COLORS.reset} pour le mode développement`)
750
+ console.log(` 2. Éditer les fichiers dans ${COLORS.cyan}src/${COLORS.reset}`)
223
751
  console.log('')
224
752
  }
225
753
 
@@ -290,11 +818,13 @@ async function cmdBuild(options) {
290
818
 
291
819
  const duration = Date.now() - startTime
292
820
 
293
- console.log('')
294
- if (errorCount === 0) {
295
- logSuccess(`${successCount} fichier(s) compilé(s) en ${duration}ms`)
296
- } else {
297
- logWarning(`${successCount} succès, ${errorCount} erreur(s) en ${duration}ms`)
821
+ if (!options.quiet) {
822
+ console.log('')
823
+ if (errorCount === 0) {
824
+ logSuccess(`${successCount} fichier(s) compilé(s) en ${duration}ms`)
825
+ } else {
826
+ logWarning(`${successCount} succès, ${errorCount} erreur(s) en ${duration}ms`)
827
+ }
298
828
  }
299
829
  }
300
830
 
@@ -304,16 +834,35 @@ async function cmdDev(options) {
304
834
  const config = loadConfig(options.config)
305
835
  const srcDir = path.resolve(process.cwd(), config.src)
306
836
  const outDir = path.resolve(process.cwd(), options.output || config.out)
837
+ const port = options.port || config.port || 3000
307
838
 
308
839
  if (!fs.existsSync(srcDir)) {
309
840
  logError(`Dossier source introuvable: ${srcDir}`)
310
841
  process.exit(1)
311
842
  }
312
843
 
844
+ if (!fs.existsSync(outDir)) {
845
+ fs.mkdirSync(outDir, { recursive: true })
846
+ }
847
+
313
848
  logInfo(`Surveillance de ${srcDir}`)
314
849
  logInfo(`Sortie vers ${outDir}`)
850
+ console.log('')
315
851
 
316
- await cmdBuild({ ...options, quiet: true })
852
+ try {
853
+ await cmdBuild({ ...options, quiet: true })
854
+ } catch (err) {
855
+ logWarning(`Erreur lors de la compilation initiale: ${err.message}`)
856
+ }
857
+
858
+ let devServer = null
859
+ if (!options.noServer) {
860
+ devServer = createDevServer(outDir, port)
861
+ console.log('')
862
+ }
863
+
864
+ logInfo('En attente de modifications... (Ctrl+C pour arrêter)')
865
+ console.log('')
317
866
 
318
867
  const watcher = new Watcher(srcDir, config.watch)
319
868
  const compiler = new EtherCompiler(config)
@@ -338,6 +887,10 @@ async function cmdDev(options) {
338
887
  fs.writeFileSync(outPath, output.content)
339
888
  logSuccess(`→ ${output.path}`)
340
889
  }
890
+
891
+ if (devServer) {
892
+ devServer.reload()
893
+ }
341
894
  } catch (err) {
342
895
  logError(err.message)
343
896
  }
@@ -363,6 +916,10 @@ async function cmdDev(options) {
363
916
  fs.writeFileSync(outPath, output.content)
364
917
  logSuccess(`→ ${output.path}`)
365
918
  }
919
+
920
+ if (devServer) {
921
+ devServer.reload()
922
+ }
366
923
  } catch (err) {
367
924
  logError(err.message)
368
925
  }
@@ -379,6 +936,9 @@ async function cmdDev(options) {
379
936
  console.log('')
380
937
  logInfo('Arrêt du mode développement')
381
938
  watcher.stop()
939
+ if (devServer) {
940
+ devServer.close()
941
+ }
382
942
  process.exit(0)
383
943
  })
384
944
  }
@@ -457,9 +1017,11 @@ function parseArgs(args) {
457
1017
  command: null,
458
1018
  config: null,
459
1019
  output: null,
1020
+ port: null,
460
1021
  verbose: false,
461
1022
  quiet: false,
462
- watch: false
1023
+ watch: false,
1024
+ noServer: false
463
1025
  }
464
1026
 
465
1027
  let i = 0
@@ -470,6 +1032,8 @@ function parseArgs(args) {
470
1032
  options.config = args[++i]
471
1033
  } else if (arg === '-o' || arg === '--output') {
472
1034
  options.output = args[++i]
1035
+ } else if (arg === '-p' || arg === '--port') {
1036
+ options.port = parseInt(args[++i], 10)
473
1037
  } else if (arg === '-v' || arg === '--verbose') {
474
1038
  options.verbose = true
475
1039
  } else if (arg === '-q' || arg === '--quiet') {
@@ -480,6 +1044,8 @@ function parseArgs(args) {
480
1044
  for (const key in COLORS) {
481
1045
  COLORS[key] = ''
482
1046
  }
1047
+ } else if (arg === '--no-server') {
1048
+ options.noServer = true
483
1049
  } else if (!arg.startsWith('-')) {
484
1050
  if (!options.command) {
485
1051
  options.command = arg
package/ether-parser.js CHANGED
@@ -349,7 +349,13 @@ class EtherParser {
349
349
  'html': 'html',
350
350
  'document': 'html',
351
351
  'tete': 'head',
352
- 'titre': 'h1',
352
+ 'titre': 'title',
353
+ 'titre1': 'h1',
354
+ 'titre2': 'h2',
355
+ 'titre3': 'h3',
356
+ 'titre4': 'h4',
357
+ 'titre5': 'h5',
358
+ 'titre6': 'h6',
353
359
  'paragraphe': 'p',
354
360
  'lien': 'a',
355
361
  'image': 'img',
@@ -871,7 +877,7 @@ class EtherParser {
871
877
  'section': 'section',
872
878
  'article': 'article',
873
879
  'cote': 'aside',
874
- 'titre': 'h1',
880
+ 'titre': 'title',
875
881
  'titre1': 'h1',
876
882
  'titre2': 'h2',
877
883
  'titre3': 'h3',
@@ -374,6 +374,7 @@ class HTMLGenerator {
374
374
  switch (node.type) {
375
375
  case 'Document':
376
376
  case 'document':
377
+ this.output += '<!DOCTYPE html>\n'
377
378
  if (node.children) {
378
379
  for (const child of node.children) {
379
380
  this.generateNode(child)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.2.0",
3
+ "version": "0.2.4",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {