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 +593 -27
- package/ether-parser.js +8 -2
- package/generators/html-generator.js +1 -0
- package/package.json +1 -1
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.
|
|
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 +
|
|
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}#
|
|
89
|
-
ether
|
|
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
|
|
370
|
+
const exampleHtml = `// cible: html
|
|
192
371
|
|
|
193
372
|
document
|
|
194
373
|
tete
|
|
195
|
-
|
|
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
|
-
|
|
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
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
+
"<!DOCTYPE html>\\n<html>\\n <body>\\n <h1>Bonjour</h1>\\n <p>Le monde</p>\\n </body>\\n</html>"
|
|
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,
|
|
213
|
-
logSuccess('Fichier
|
|
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.
|
|
221
|
-
console.log(` 2.
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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': '
|
|
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': '
|
|
880
|
+
'titre': 'title',
|
|
875
881
|
'titre1': 'h1',
|
|
876
882
|
'titre2': 'h2',
|
|
877
883
|
'titre3': 'h3',
|