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 +580 -19
- 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.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 +
|
|
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,29 +367,379 @@ 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('')
|
|
@@ -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.
|
|
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': '
|
|
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',
|