ether-code 0.1.8 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {
@@ -37,8 +37,9 @@
37
37
  "cli/",
38
38
  "generators/",
39
39
  "lexer/",
40
- "parsers/",
41
40
  "i18n/",
41
+ "ether-parser.js",
42
+ "ether-compiler.js",
42
43
  "README.md",
43
44
  "LICENSE"
44
45
  ]
package/cli/ether.js DELETED
@@ -1,700 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
- const http = require('http')
6
- const { EtherCompiler } = require('./compiler')
7
- const { Watcher } = require('./watcher')
8
-
9
- const VERSION = '0.1.8'
10
-
11
- const COLORS = {
12
- reset: '\x1b[0m',
13
- bright: '\x1b[1m',
14
- dim: '\x1b[2m',
15
- red: '\x1b[31m',
16
- green: '\x1b[32m',
17
- yellow: '\x1b[33m',
18
- blue: '\x1b[34m',
19
- magenta: '\x1b[35m',
20
- cyan: '\x1b[36m'
21
- }
22
-
23
- function log(msg, color = '') {
24
- console.log(`${color}${msg}${COLORS.reset}`)
25
- }
26
-
27
- function logSuccess(msg) {
28
- log(`✓ ${msg}`, COLORS.green)
29
- }
30
-
31
- function logError(msg) {
32
- log(`✗ ${msg}`, COLORS.red)
33
- }
34
-
35
- function logInfo(msg) {
36
- log(`ℹ ${msg}`, COLORS.cyan)
37
- }
38
-
39
- function logWarning(msg) {
40
- log(`⚠ ${msg}`, COLORS.yellow)
41
- }
42
-
43
- function showBanner() {
44
- console.log(`
45
- ${COLORS.cyan}${COLORS.bright}
46
- ███████╗████████╗██╗ ██╗███████╗██████╗
47
- ██╔════╝╚══██╔══╝██║ ██║██╔════╝██╔══██╗
48
- █████╗ ██║ ███████║█████╗ ██████╔╝
49
- ██╔══╝ ██║ ██╔══██║██╔══╝ ██╔══██╗
50
- ███████╗ ██║ ██║ ██║███████╗██║ ██║
51
- ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
52
- ${COLORS.reset}
53
- ${COLORS.dim}Le langage intentionnel - v${VERSION}${COLORS.reset}
54
- `)
55
- }
56
-
57
- function showHelp() {
58
- showBanner()
59
- console.log(`
60
- ${COLORS.bright}UTILISATION${COLORS.reset}
61
- ether <commande> [options]
62
-
63
- ${COLORS.bright}COMMANDES${COLORS.reset}
64
- ${COLORS.cyan}init${COLORS.reset} Initialiser un nouveau projet Ether
65
- ${COLORS.cyan}build${COLORS.reset} Compiler les fichiers .eth
66
- ${COLORS.cyan}dev${COLORS.reset} Mode développement (serveur + watch + live reload)
67
- ${COLORS.cyan}check${COLORS.reset} Vérifier la syntaxe sans compiler
68
- ${COLORS.cyan}help${COLORS.reset} Afficher cette aide
69
- ${COLORS.cyan}version${COLORS.reset} Afficher la version
70
-
71
- ${COLORS.bright}OPTIONS${COLORS.reset}
72
- -c, --config Chemin vers le fichier de configuration
73
- -o, --output Dossier de sortie
74
- -p, --port Port du serveur de développement (défaut: 3000)
75
- -w, --watch Surveiller les changements (alias de dev)
76
- -v, --verbose Mode verbeux
77
- -q, --quiet Mode silencieux
78
- --no-color Désactiver les couleurs
79
-
80
- ${COLORS.bright}EXEMPLES${COLORS.reset}
81
- ${COLORS.dim}# Initialiser un projet${COLORS.reset}
82
- ether init
83
-
84
- ${COLORS.dim}# Compiler le projet${COLORS.reset}
85
- ether build
86
-
87
- ${COLORS.dim}# Mode développement sur localhost:3000${COLORS.reset}
88
- ether dev
89
-
90
- ${COLORS.dim}# Mode développement sur un autre port${COLORS.reset}
91
- ether dev -p 8080
92
-
93
- ${COLORS.bright}DOCUMENTATION${COLORS.reset}
94
- https://ether-code.com/docs
95
- `)
96
- }
97
-
98
- function showVersion() {
99
- console.log(`Ether v${VERSION}`)
100
- }
101
-
102
- function loadConfig(configPath) {
103
- const defaultConfig = {
104
- src: './src',
105
- out: './dist',
106
- targets: {},
107
- i18n: 'fr',
108
- watch: {
109
- ignored: ['node_modules', '.git', 'dist']
110
- }
111
- }
112
-
113
- const configFile = configPath || path.join(process.cwd(), 'ether.config.js')
114
-
115
- if (fs.existsSync(configFile)) {
116
- try {
117
- const userConfig = require(configFile)
118
- return { ...defaultConfig, ...userConfig }
119
- } catch (err) {
120
- logError(`Erreur de configuration: ${err.message}`)
121
- return defaultConfig
122
- }
123
- }
124
-
125
- const jsonConfig = path.join(process.cwd(), 'ether.config.json')
126
- if (fs.existsSync(jsonConfig)) {
127
- try {
128
- const content = fs.readFileSync(jsonConfig, 'utf-8')
129
- const userConfig = JSON.parse(content)
130
- return { ...defaultConfig, ...userConfig }
131
- } catch (err) {
132
- logError(`Erreur de configuration: ${err.message}`)
133
- return defaultConfig
134
- }
135
- }
136
-
137
- return defaultConfig
138
- }
139
-
140
- async function cmdInit() {
141
- showBanner()
142
- logInfo('Initialisation du projet Ether...')
143
-
144
- const cwd = process.cwd()
145
-
146
- const srcDir = path.join(cwd, 'src')
147
- if (!fs.existsSync(srcDir)) {
148
- fs.mkdirSync(srcDir, { recursive: true })
149
- logSuccess('Dossier src/ créé')
150
- }
151
-
152
- const publicDir = path.join(cwd, 'public')
153
- if (!fs.existsSync(publicDir)) {
154
- fs.mkdirSync(publicDir, { recursive: true })
155
- logSuccess('Dossier public/ créé')
156
- }
157
-
158
- const imagesDir = path.join(publicDir, 'images')
159
- if (!fs.existsSync(imagesDir)) {
160
- fs.mkdirSync(imagesDir, { recursive: true })
161
- logSuccess('Dossier public/images/ créé')
162
- }
163
-
164
- const videosDir = path.join(publicDir, 'videos')
165
- if (!fs.existsSync(videosDir)) {
166
- fs.mkdirSync(videosDir, { recursive: true })
167
- logSuccess('Dossier public/videos/ créé')
168
- }
169
-
170
- const configContent = `module.exports = {
171
- src: './src',
172
- out: './dist',
173
-
174
- i18n: 'fr',
175
-
176
- targets: {
177
- css: {
178
- extension: '.css',
179
- minify: false
180
- },
181
- html: {
182
- extension: '.html',
183
- minify: false
184
- },
185
- js: {
186
- extension: '.js',
187
- minify: false
188
- }
189
- },
190
-
191
- watch: {
192
- ignored: ['node_modules', '.git', 'dist']
193
- }
194
- }
195
- `
196
-
197
- const configPath = path.join(cwd, 'ether.config.js')
198
- if (!fs.existsSync(configPath)) {
199
- fs.writeFileSync(configPath, configContent)
200
- logSuccess('Fichier ether.config.js créé')
201
- } else {
202
- logWarning('ether.config.js existe déjà')
203
- }
204
-
205
- const projectName = path.basename(cwd)
206
- const packageJson = {
207
- name: projectName,
208
- version: "1.0.0",
209
- description: "Projet Ether",
210
- scripts: {
211
- dev: "ether dev",
212
- build: "ether build",
213
- check: "ether check"
214
- },
215
- keywords: ["ether"],
216
- license: "MIT"
217
- }
218
-
219
- const packagePath = path.join(cwd, 'package.json')
220
- if (!fs.existsSync(packagePath)) {
221
- fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2))
222
- logSuccess('Fichier package.json créé')
223
- } else {
224
- logWarning('package.json existe déjà')
225
- }
226
-
227
- const exampleEth = `html
228
- tête
229
- titre "Ma première page Ether"
230
- corps
231
- entete
232
- titre1 "Bienvenue sur Ether"
233
- paragraphe "Le langage intentionnel"
234
- principal
235
- section #hero
236
- titre2 "Commencez maintenant"
237
- paragraphe "Programmez dans votre langue naturelle."
238
- bouton .primary "Démarrer"
239
- pied
240
- paragraphe "© 2025 Créé avec Ether"
241
- `
242
-
243
- const examplePath = path.join(srcDir, 'index.eth')
244
- if (!fs.existsSync(examplePath)) {
245
- fs.writeFileSync(examplePath, exampleEth)
246
- logSuccess('Fichier exemple src/index.eth créé')
247
- }
248
-
249
- console.log('')
250
- logSuccess('Projet initialisé avec succès!')
251
- console.log('')
252
- logInfo('Prochaines étapes:')
253
- console.log(` 1. Éditer ${COLORS.cyan}src/index.eth${COLORS.reset}`)
254
- console.log(` 2. Lancer ${COLORS.cyan}ether dev${COLORS.reset} pour le mode développement`)
255
- console.log(` 3. Ou ${COLORS.cyan}ether build${COLORS.reset} pour compiler`)
256
- console.log('')
257
- }
258
-
259
- async function cmdBuild(options) {
260
- const startTime = Date.now()
261
-
262
- if (!options.quiet) {
263
- showBanner()
264
- logInfo('Compilation en cours...')
265
- console.log('')
266
- }
267
-
268
- const config = loadConfig(options.config)
269
- const srcDir = path.resolve(process.cwd(), config.src)
270
- const outDir = path.resolve(process.cwd(), options.output || config.out)
271
- const publicDir = path.resolve(process.cwd(), config.public || 'public')
272
-
273
- if (!fs.existsSync(srcDir)) {
274
- logError(`Dossier source introuvable: ${srcDir}`)
275
- process.exit(1)
276
- }
277
-
278
- if (!fs.existsSync(outDir)) {
279
- fs.mkdirSync(outDir, { recursive: true })
280
- }
281
-
282
- if (fs.existsSync(publicDir)) {
283
- copyDir(publicDir, outDir)
284
- if (!options.quiet) {
285
- logSuccess('Dossier public/ copié vers dist/')
286
- }
287
- }
288
-
289
- const compiler = new EtherCompiler(config)
290
-
291
- const files = findEthFiles(srcDir)
292
-
293
- if (files.length === 0) {
294
- logWarning('Aucun fichier .eth trouvé')
295
- return
296
- }
297
-
298
- let successCount = 0
299
- let errorCount = 0
300
-
301
- for (const file of files) {
302
- try {
303
- const relativePath = path.relative(srcDir, file)
304
- const result = await compiler.compileFile(file)
305
-
306
- for (const output of result.outputs) {
307
- const outPath = path.join(outDir, output.path)
308
- const outDirPath = path.dirname(outPath)
309
-
310
- if (!fs.existsSync(outDirPath)) {
311
- fs.mkdirSync(outDirPath, { recursive: true })
312
- }
313
-
314
- fs.writeFileSync(outPath, output.content)
315
-
316
- if (options.verbose) {
317
- logSuccess(`${relativePath} → ${output.path}`)
318
- }
319
- }
320
-
321
- successCount++
322
- } catch (err) {
323
- errorCount++
324
- logError(`${path.relative(srcDir, file)}: ${err.message}`)
325
-
326
- if (options.verbose) {
327
- console.error(err.stack)
328
- }
329
- }
330
- }
331
-
332
- const duration = Date.now() - startTime
333
-
334
- console.log('')
335
- if (errorCount === 0) {
336
- logSuccess(`${successCount} fichier(s) compilé(s) en ${duration}ms`)
337
- } else {
338
- logWarning(`${successCount} succès, ${errorCount} erreur(s) en ${duration}ms`)
339
- }
340
- }
341
-
342
- async function cmdDev(options) {
343
- showBanner()
344
-
345
- const config = loadConfig(options.config)
346
- const srcDir = path.resolve(process.cwd(), config.src)
347
- const outDir = path.resolve(process.cwd(), options.output || config.out)
348
- const port = options.port || config.port || 3000
349
-
350
- if (!fs.existsSync(srcDir)) {
351
- logError(`Dossier source introuvable: ${srcDir}`)
352
- process.exit(1)
353
- }
354
-
355
- logInfo(`Surveillance de ${srcDir}`)
356
- logInfo(`Sortie vers ${outDir}`)
357
- console.log('')
358
-
359
- await cmdBuild({ ...options, quiet: true })
360
-
361
- const clients = []
362
-
363
- const MIME_TYPES = {
364
- '.html': 'text/html',
365
- '.css': 'text/css',
366
- '.js': 'application/javascript',
367
- '.json': 'application/json',
368
- '.png': 'image/png',
369
- '.jpg': 'image/jpeg',
370
- '.jpeg': 'image/jpeg',
371
- '.gif': 'image/gif',
372
- '.svg': 'image/svg+xml',
373
- '.ico': 'image/x-icon',
374
- '.webp': 'image/webp',
375
- '.mp4': 'video/mp4',
376
- '.webm': 'video/webm',
377
- '.woff': 'font/woff',
378
- '.woff2': 'font/woff2',
379
- '.ttf': 'font/ttf'
380
- }
381
-
382
- const LIVE_RELOAD_SCRIPT = `
383
- <script>
384
- (function() {
385
- const es = new EventSource('/__ether_reload');
386
- es.onmessage = function(e) {
387
- if (e.data === 'reload') {
388
- window.location.reload();
389
- }
390
- };
391
- es.onerror = function() {
392
- es.close();
393
- setTimeout(function() { window.location.reload(); }, 1000);
394
- };
395
- })();
396
- </script>
397
- </body>`
398
-
399
- const server = http.createServer((req, res) => {
400
- if (req.url === '/__ether_reload') {
401
- res.writeHead(200, {
402
- 'Content-Type': 'text/event-stream',
403
- 'Cache-Control': 'no-cache',
404
- 'Connection': 'keep-alive',
405
- 'Access-Control-Allow-Origin': '*'
406
- })
407
- res.write('data: connected\n\n')
408
- clients.push(res)
409
- req.on('close', () => {
410
- const index = clients.indexOf(res)
411
- if (index > -1) clients.splice(index, 1)
412
- })
413
- return
414
- }
415
-
416
- let filePath = req.url === '/' ? '/index.html' : req.url
417
- filePath = filePath.split('?')[0]
418
- filePath = path.join(outDir, filePath)
419
-
420
- const ext = path.extname(filePath).toLowerCase()
421
- const contentType = MIME_TYPES[ext] || 'application/octet-stream'
422
-
423
- fs.readFile(filePath, (err, content) => {
424
- if (err) {
425
- if (err.code === 'ENOENT') {
426
- res.writeHead(404)
427
- res.end('404 - Fichier non trouvé')
428
- } else {
429
- res.writeHead(500)
430
- res.end('Erreur serveur')
431
- }
432
- return
433
- }
434
-
435
- if (ext === '.html') {
436
- content = content.toString().replace('</body>', LIVE_RELOAD_SCRIPT)
437
- }
438
-
439
- res.writeHead(200, { 'Content-Type': contentType })
440
- res.end(content)
441
- })
442
- })
443
-
444
- server.listen(port, () => {
445
- logSuccess(`Serveur démarré sur http://localhost:${port}`)
446
- console.log('')
447
- logInfo('En attente de modifications... (Ctrl+C pour arrêter)')
448
- console.log('')
449
- })
450
-
451
- function notifyClients() {
452
- clients.forEach(client => {
453
- client.write('data: reload\n\n')
454
- })
455
- }
456
-
457
- const watcher = new Watcher(srcDir, config.watch)
458
- const compiler = new EtherCompiler(config)
459
-
460
- watcher.on('change', async (filePath) => {
461
- if (!filePath.endsWith('.eth')) return
462
-
463
- const relativePath = path.relative(srcDir, filePath)
464
- logInfo(`Modification: ${relativePath}`)
465
-
466
- try {
467
- const result = await compiler.compileFile(filePath)
468
-
469
- for (const output of result.outputs) {
470
- const outPath = path.join(outDir, output.path)
471
- const outDirPath = path.dirname(outPath)
472
-
473
- if (!fs.existsSync(outDirPath)) {
474
- fs.mkdirSync(outDirPath, { recursive: true })
475
- }
476
-
477
- fs.writeFileSync(outPath, output.content)
478
- logSuccess(`→ ${output.path}`)
479
- }
480
-
481
- notifyClients()
482
- } catch (err) {
483
- logError(err.message)
484
- }
485
- })
486
-
487
- watcher.on('add', async (filePath) => {
488
- if (!filePath.endsWith('.eth')) return
489
-
490
- const relativePath = path.relative(srcDir, filePath)
491
- logInfo(`Nouveau fichier: ${relativePath}`)
492
-
493
- try {
494
- const result = await compiler.compileFile(filePath)
495
-
496
- for (const output of result.outputs) {
497
- const outPath = path.join(outDir, output.path)
498
- const outDirPath = path.dirname(outPath)
499
-
500
- if (!fs.existsSync(outDirPath)) {
501
- fs.mkdirSync(outDirPath, { recursive: true })
502
- }
503
-
504
- fs.writeFileSync(outPath, output.content)
505
- logSuccess(`→ ${output.path}`)
506
- }
507
-
508
- notifyClients()
509
- } catch (err) {
510
- logError(err.message)
511
- }
512
- })
513
-
514
- watcher.on('unlink', (filePath) => {
515
- if (!filePath.endsWith('.eth')) return
516
- logWarning(`Fichier supprimé: ${path.relative(srcDir, filePath)}`)
517
- })
518
-
519
- watcher.start()
520
-
521
- process.on('SIGINT', () => {
522
- console.log('')
523
- logInfo('Arrêt du serveur de développement')
524
- server.close()
525
- watcher.stop()
526
- process.exit(0)
527
- })
528
- }
529
-
530
- async function cmdCheck(options) {
531
- showBanner()
532
- logInfo('Vérification de la syntaxe...')
533
- console.log('')
534
-
535
- const config = loadConfig(options.config)
536
- const srcDir = path.resolve(process.cwd(), config.src)
537
-
538
- if (!fs.existsSync(srcDir)) {
539
- logError(`Dossier source introuvable: ${srcDir}`)
540
- process.exit(1)
541
- }
542
-
543
- const compiler = new EtherCompiler(config)
544
- const files = findEthFiles(srcDir)
545
-
546
- if (files.length === 0) {
547
- logWarning('Aucun fichier .eth trouvé')
548
- return
549
- }
550
-
551
- let validCount = 0
552
- let errorCount = 0
553
-
554
- for (const file of files) {
555
- const relativePath = path.relative(srcDir, file)
556
-
557
- try {
558
- await compiler.check(file)
559
- logSuccess(relativePath)
560
- validCount++
561
- } catch (err) {
562
- logError(`${relativePath}: ${err.message}`)
563
- errorCount++
564
- }
565
- }
566
-
567
- console.log('')
568
- if (errorCount === 0) {
569
- logSuccess(`${validCount} fichier(s) valide(s)`)
570
- } else {
571
- logError(`${errorCount} erreur(s) sur ${files.length} fichier(s)`)
572
- process.exit(1)
573
- }
574
- }
575
-
576
- function findEthFiles(dir) {
577
- const files = []
578
-
579
- function scan(currentDir) {
580
- const entries = fs.readdirSync(currentDir, { withFileTypes: true })
581
-
582
- for (const entry of entries) {
583
- const fullPath = path.join(currentDir, entry.name)
584
-
585
- if (entry.isDirectory()) {
586
- if (!['node_modules', '.git', 'dist'].includes(entry.name)) {
587
- scan(fullPath)
588
- }
589
- } else if (entry.name.endsWith('.eth')) {
590
- files.push(fullPath)
591
- }
592
- }
593
- }
594
-
595
- scan(dir)
596
- return files
597
- }
598
-
599
- function copyDir(src, dest) {
600
- if (!fs.existsSync(dest)) {
601
- fs.mkdirSync(dest, { recursive: true })
602
- }
603
-
604
- const entries = fs.readdirSync(src, { withFileTypes: true })
605
-
606
- for (const entry of entries) {
607
- const srcPath = path.join(src, entry.name)
608
- const destPath = path.join(dest, entry.name)
609
-
610
- if (entry.isDirectory()) {
611
- copyDir(srcPath, destPath)
612
- } else {
613
- fs.copyFileSync(srcPath, destPath)
614
- }
615
- }
616
- }
617
-
618
- function parseArgs(args) {
619
- const options = {
620
- command: null,
621
- config: null,
622
- output: null,
623
- port: null,
624
- verbose: false,
625
- quiet: false,
626
- watch: false
627
- }
628
-
629
- let i = 0
630
- while (i < args.length) {
631
- const arg = args[i]
632
-
633
- if (arg === '-c' || arg === '--config') {
634
- options.config = args[++i]
635
- } else if (arg === '-o' || arg === '--output') {
636
- options.output = args[++i]
637
- } else if (arg === '-p' || arg === '--port') {
638
- options.port = parseInt(args[++i], 10)
639
- } else if (arg === '-v' || arg === '--verbose') {
640
- options.verbose = true
641
- } else if (arg === '-q' || arg === '--quiet') {
642
- options.quiet = true
643
- } else if (arg === '-w' || arg === '--watch') {
644
- options.watch = true
645
- } else if (arg === '--no-color') {
646
- for (const key in COLORS) {
647
- COLORS[key] = ''
648
- }
649
- } else if (!arg.startsWith('-')) {
650
- if (!options.command) {
651
- options.command = arg
652
- }
653
- }
654
-
655
- i++
656
- }
657
-
658
- return options
659
- }
660
-
661
- async function main() {
662
- const args = process.argv.slice(2)
663
- const options = parseArgs(args)
664
-
665
- if (options.watch && !options.command) {
666
- options.command = 'dev'
667
- }
668
-
669
- switch (options.command) {
670
- case 'init':
671
- await cmdInit()
672
- break
673
- case 'build':
674
- await cmdBuild(options)
675
- break
676
- case 'dev':
677
- case 'watch':
678
- await cmdDev(options)
679
- break
680
- case 'check':
681
- await cmdCheck(options)
682
- break
683
- case 'version':
684
- case '-V':
685
- case '--version':
686
- showVersion()
687
- break
688
- case 'help':
689
- case '-h':
690
- case '--help':
691
- default:
692
- showHelp()
693
- break
694
- }
695
- }
696
-
697
- main().catch(err => {
698
- logError(err.message)
699
- process.exit(1)
700
- })