ether-code 0.2.0 → 0.2.3

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.3'
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,29 +367,379 @@ 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('')
@@ -219,7 +748,7 @@ document
219
748
  logInfo('Prochaines étapes:')
220
749
  console.log(` 1. Éditer ${COLORS.cyan}src/index.eth${COLORS.reset}`)
221
750
  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`)
751
+ console.log(` 3. Ouvrir ${COLORS.cyan}http://localhost:3000${COLORS.reset} dans le navigateur`)
223
752
  console.log('')
224
753
  }
225
754
 
@@ -304,17 +833,32 @@ async function cmdDev(options) {
304
833
  const config = loadConfig(options.config)
305
834
  const srcDir = path.resolve(process.cwd(), config.src)
306
835
  const outDir = path.resolve(process.cwd(), options.output || config.out)
836
+ const port = options.port || config.port || 3000
307
837
 
308
838
  if (!fs.existsSync(srcDir)) {
309
839
  logError(`Dossier source introuvable: ${srcDir}`)
310
840
  process.exit(1)
311
841
  }
312
842
 
843
+ if (!fs.existsSync(outDir)) {
844
+ fs.mkdirSync(outDir, { recursive: true })
845
+ }
846
+
313
847
  logInfo(`Surveillance de ${srcDir}`)
314
848
  logInfo(`Sortie vers ${outDir}`)
849
+ console.log('')
315
850
 
316
851
  await cmdBuild({ ...options, quiet: true })
317
852
 
853
+ let devServer = null
854
+ if (!options.noServer) {
855
+ devServer = createDevServer(outDir, port)
856
+ }
857
+
858
+ console.log('')
859
+ logInfo('En attente de modifications... (Ctrl+C pour arrêter)')
860
+ console.log('')
861
+
318
862
  const watcher = new Watcher(srcDir, config.watch)
319
863
  const compiler = new EtherCompiler(config)
320
864
 
@@ -338,6 +882,10 @@ async function cmdDev(options) {
338
882
  fs.writeFileSync(outPath, output.content)
339
883
  logSuccess(`→ ${output.path}`)
340
884
  }
885
+
886
+ if (devServer) {
887
+ devServer.reload()
888
+ }
341
889
  } catch (err) {
342
890
  logError(err.message)
343
891
  }
@@ -363,6 +911,10 @@ async function cmdDev(options) {
363
911
  fs.writeFileSync(outPath, output.content)
364
912
  logSuccess(`→ ${output.path}`)
365
913
  }
914
+
915
+ if (devServer) {
916
+ devServer.reload()
917
+ }
366
918
  } catch (err) {
367
919
  logError(err.message)
368
920
  }
@@ -379,6 +931,9 @@ async function cmdDev(options) {
379
931
  console.log('')
380
932
  logInfo('Arrêt du mode développement')
381
933
  watcher.stop()
934
+ if (devServer) {
935
+ devServer.close()
936
+ }
382
937
  process.exit(0)
383
938
  })
384
939
  }
@@ -457,9 +1012,11 @@ function parseArgs(args) {
457
1012
  command: null,
458
1013
  config: null,
459
1014
  output: null,
1015
+ port: null,
460
1016
  verbose: false,
461
1017
  quiet: false,
462
- watch: false
1018
+ watch: false,
1019
+ noServer: false
463
1020
  }
464
1021
 
465
1022
  let i = 0
@@ -470,6 +1027,8 @@ function parseArgs(args) {
470
1027
  options.config = args[++i]
471
1028
  } else if (arg === '-o' || arg === '--output') {
472
1029
  options.output = args[++i]
1030
+ } else if (arg === '-p' || arg === '--port') {
1031
+ options.port = parseInt(args[++i], 10)
473
1032
  } else if (arg === '-v' || arg === '--verbose') {
474
1033
  options.verbose = true
475
1034
  } else if (arg === '-q' || arg === '--quiet') {
@@ -480,6 +1039,8 @@ function parseArgs(args) {
480
1039
  for (const key in COLORS) {
481
1040
  COLORS[key] = ''
482
1041
  }
1042
+ } else if (arg === '--no-server') {
1043
+ options.noServer = true
483
1044
  } else if (!arg.startsWith('-')) {
484
1045
  if (!options.command) {
485
1046
  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.3",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {