ether-code 0.1.3 → 0.1.5

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/compiler.js CHANGED
@@ -126,11 +126,15 @@ class EtherCompiler {
126
126
  }
127
127
 
128
128
  parse(content, target) {
129
+ const normalizedTarget = this.normalizeTarget(target)
130
+
131
+ if (normalizedTarget === 'html') {
132
+ return this.parseHTML(content)
133
+ }
134
+
129
135
  const lexer = new EtherLexer(content)
130
136
  const tokens = lexer.tokenize()
131
137
 
132
- const normalizedTarget = this.normalizeTarget(target)
133
-
134
138
  try {
135
139
  const ParserClass = this.loadParser(normalizedTarget)
136
140
  if (ParserClass) {
@@ -143,6 +147,13 @@ class EtherCompiler {
143
147
  return this.buildGenericAST(tokens, target)
144
148
  }
145
149
 
150
+ parseHTML(content) {
151
+ const { HTMLParser } = require('../parsers/html-parser')
152
+ const i18nPath = path.join(__dirname, '..', 'i18n', 'i18n-html.json')
153
+ const parser = new HTMLParser(i18nPath)
154
+ return parser.parse(content)
155
+ }
156
+
146
157
  loadParser(target) {
147
158
  try {
148
159
  const parserPath = path.join(__dirname, '..', 'parsers', `${target}-parser.js`)
@@ -212,7 +223,8 @@ class EtherCompiler {
212
223
  inferTargetFromContent(content) {
213
224
  const patterns = {
214
225
  html: [
215
- /\b(page|document|entête|entete|corps|section|div|paragraphe|titre|lien|image|formulaire|bouton)\b/i,
226
+ /^html\b/im,
227
+ /\b(html|page|document|entête|entete|corps|section|div|paragraphe|titre|lien|image|formulaire|bouton)\b/i,
216
228
  /\b(balise|élément|element|attribut)\b/i
217
229
  ],
218
230
  css: [
@@ -222,7 +234,7 @@ class EtherCompiler {
222
234
  ],
223
235
  js: [
224
236
  /\b(fonction|variable|constante|si|sinon|pour|tant que|retourner)\b/i,
225
- /\b(classe|méthode|methode|constructeur)\b/i,
237
+ /\b(classe|méthode|constructeur)\b/i,
226
238
  /=>\s*\{/
227
239
  ],
228
240
  php: [
@@ -230,7 +242,7 @@ class EtherCompiler {
230
242
  /\$\w+/
231
243
  ],
232
244
  sql: [
233
- /\b(sélectionner|selectionner|insérer|inserer|mettre à jour|mettre a jour|supprimer|créer table|creer table|depuis|où|ou)\b/i,
245
+ /\b(sélectionner|insérer|mettre à jour|supprimer|créer table|depuis|)\b/i,
234
246
  /\b(SELECT|INSERT|UPDATE|DELETE|CREATE|FROM|WHERE)\b/i
235
247
  ],
236
248
  python: [
@@ -238,7 +250,7 @@ class EtherCompiler {
238
250
  /:\s*$/m
239
251
  ],
240
252
  react: [
241
- /\b(composant|état|etat|effet|props|rendu)\b/i,
253
+ /\b(composant|état|effet|props|rendu)\b/i,
242
254
  /<[\w]+[^>]*\/>/
243
255
  ],
244
256
  graphql: [
@@ -295,4 +307,4 @@ class EtherCompiler {
295
307
  }
296
308
  }
297
309
 
298
- module.exports = { EtherCompiler, GENERATORS, TARGET_EXTENSIONS }
310
+ module.exports = { EtherCompiler, GENERATORS, TARGET_EXTENSIONS }
package/cli/ether.js CHANGED
@@ -6,7 +6,7 @@ const http = require('http')
6
6
  const { EtherCompiler } = require('./compiler')
7
7
  const { Watcher } = require('./watcher')
8
8
 
9
- const VERSION = '0.1.3'
9
+ const VERSION = '0.1.5'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
@@ -91,7 +91,7 @@ ${COLORS.bright}EXEMPLES${COLORS.reset}
91
91
  ether dev -p 8080
92
92
 
93
93
  ${COLORS.bright}DOCUMENTATION${COLORS.reset}
94
- https://docs.ether-code.com
94
+ https://ether-code.com/docs
95
95
  `)
96
96
  }
97
97
 
@@ -224,33 +224,20 @@ async function cmdInit() {
224
224
  logWarning('package.json existe déjà')
225
225
  }
226
226
 
227
- const exampleEth = `// Exemple de fichier Ether
228
- // Cible: html
229
-
230
- page "Ma première page Ether"
231
- entête
232
- titre "Bienvenue"
233
-
227
+ const exampleEth = `html
228
+ tête
229
+ titre "Ma première page Ether"
234
230
  corps
235
- section #hero
236
- titre "Ether"
231
+ entete
232
+ titre1 "Bienvenue sur Ether"
237
233
  paragraphe "Le langage intentionnel"
238
- bouton .primary "Commencer"
239
- section #features
240
- article
241
- icône "🚀"
242
- titre "Rapide"
243
- paragraphe "Compilez votre code en un clin d'œil."
244
- article
245
- icône "🔒"
246
- titre "Sécurisé"
247
- paragraphe "Vos données sont protégées par défaut."
248
- article
249
- icône "🌐"
250
- titre "Évolutif"
251
- paragraphe "Adaptez votre application à vos besoins."
252
- piedDePage
253
- paragraphe "© 2025 Site créé avec Ether. Tous droits réservés."
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"
254
241
  `
255
242
 
256
243
  const examplePath = path.join(srcDir, 'index.eth')
@@ -710,4 +697,4 @@ async function main() {
710
697
  main().catch(err => {
711
698
  logError(err.message)
712
699
  process.exit(1)
713
- })
700
+ })
@@ -455,10 +455,16 @@ class HTMLGenerator {
455
455
  if (!node) return
456
456
 
457
457
  switch (node.type) {
458
+ case 'Document':
459
+ this.generateDocument(node)
460
+ break
458
461
  case 'Element':
459
462
  case 'élément':
460
463
  this.generateElement(node)
461
464
  break
465
+ case 'TextNode':
466
+ this.generateTextNode(node)
467
+ break
462
468
  case 'Text':
463
469
  case 'text':
464
470
  this.generateText(node)
@@ -474,14 +480,34 @@ class HTMLGenerator {
474
480
  default:
475
481
  if (node.tag || node.tagName) {
476
482
  this.generateElement(node)
477
- } else if (node.text || node.content) {
483
+ } else if (node.text || node.content || node.value) {
478
484
  this.generateText(node)
479
485
  }
480
486
  }
481
487
  }
482
488
 
489
+ generateDocument(node) {
490
+ this.writeLine('<!DOCTYPE html>')
491
+ if (node.html) {
492
+ this.generateNode(node.html)
493
+ }
494
+ }
495
+
496
+ generateTextNode(node) {
497
+ if (node.value || node.content) {
498
+ this.writeLine(this.escapeHtml(node.value || node.content))
499
+ }
500
+ }
501
+
483
502
  generateElement(node) {
484
- const tag = this.translateTag(node.tag || node.tagName || node.name)
503
+ let tag
504
+ if (node.htmlTag && !node.htmlTag.startsWith('<')) {
505
+ tag = node.htmlTag
506
+ } else {
507
+ const rawTag = node.tag || node.tagName || node.name
508
+ tag = this.translateTag(rawTag)
509
+ }
510
+
485
511
  const attributes = this.generateAttributes(node.attributes || node.attrs || {})
486
512
  const isVoid = this.voidElements.includes(tag.toLowerCase())
487
513
 
@@ -521,30 +547,46 @@ class HTMLGenerator {
521
547
  }
522
548
 
523
549
  generateAttributes(attrs) {
524
- if (!attrs || Object.keys(attrs).length === 0) {
550
+ if (!attrs || (Array.isArray(attrs) && attrs.length === 0) || Object.keys(attrs).length === 0) {
525
551
  return ''
526
552
  }
527
553
 
528
554
  const parts = []
529
- for (const [key, value] of Object.entries(attrs)) {
530
- let attrName = this.translateAttribute(key)
531
-
532
- if (attrName.startsWith('au ') || attrName.startsWith('a la ')) {
533
- attrName = this.translateEvent(key)
534
- }
535
-
536
- if (value === true || value === '') {
537
- parts.push(attrName)
538
- } else if (value === false || value === null || value === undefined) {
539
- continue
540
- } else {
541
- let attrValue = this.translateAttrValue(value)
555
+
556
+ if (Array.isArray(attrs)) {
557
+ for (const attr of attrs) {
558
+ const attrName = attr.htmlName || this.translateAttribute(attr.name)
559
+ const value = attr.value
542
560
 
543
- if (attrName === 'type' && this.isInputType(value)) {
544
- attrValue = this.translateInputType(value)
561
+ if (value === true || value === '') {
562
+ parts.push(attrName)
563
+ } else if (value === false || value === null || value === undefined) {
564
+ continue
565
+ } else {
566
+ parts.push(`${attrName}="${this.escapeAttr(value)}"`)
545
567
  }
568
+ }
569
+ } else {
570
+ for (const [key, value] of Object.entries(attrs)) {
571
+ let attrName = this.translateAttribute(key)
546
572
 
547
- parts.push(`${attrName}="${this.escapeAttr(attrValue)}"`)
573
+ if (attrName.startsWith('au ') || attrName.startsWith('a la ')) {
574
+ attrName = this.translateEvent(key)
575
+ }
576
+
577
+ if (value === true || value === '') {
578
+ parts.push(attrName)
579
+ } else if (value === false || value === null || value === undefined) {
580
+ continue
581
+ } else {
582
+ let attrValue = this.translateAttrValue(value)
583
+
584
+ if (attrName === 'type' && this.isInputType(value)) {
585
+ attrValue = this.translateInputType(value)
586
+ }
587
+
588
+ parts.push(`${attrName}="${this.escapeAttr(attrValue)}"`)
589
+ }
548
590
  }
549
591
  }
550
592
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {
@@ -20,15 +20,15 @@
20
20
  "french",
21
21
  "multilingual"
22
22
  ],
23
- "author": "Stéphane LEGRAND",
23
+ "author": "Steve",
24
24
  "license": "MIT",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/ether-code/ether"
27
+ "url": "https://github.com/ether-lang/ether"
28
28
  },
29
29
  "homepage": "https://ether-code.com",
30
30
  "bugs": {
31
- "url": "https://github.com/ether-code/ether/issues"
31
+ "url": "https://github.com/ether-lang/ether/issues"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=14.0.0"
@@ -42,4 +42,4 @@
42
42
  "README.md",
43
43
  "LICENSE"
44
44
  ]
45
- }
45
+ }
@@ -551,6 +551,7 @@ class HTMLParser {
551
551
  const tagName = elemToken.value
552
552
  const htmlTag = this.translateElement(tagName)
553
553
  const loc = elemToken.loc
554
+ const elemIndent = loc.column - 1
554
555
 
555
556
  const attributes = this.parseAttributes()
556
557
 
@@ -567,7 +568,7 @@ class HTMLParser {
567
568
  children.push(new AST.TextNode(textContent))
568
569
  }
569
570
 
570
- const childElements = this.parseChildren()
571
+ const childElements = this.parseChildren(elemIndent)
571
572
  children = children.concat(childElements)
572
573
  }
573
574
 
@@ -575,6 +576,9 @@ class HTMLParser {
575
576
  }
576
577
 
577
578
  parseShorthandElement() {
579
+ const firstToken = this.peek()
580
+ const elemIndent = firstToken.loc ? firstToken.loc.column - 1 : 0
581
+
578
582
  const attributes = []
579
583
  let tagName = 'div'
580
584
  let htmlTag = 'div'
@@ -607,7 +611,7 @@ class HTMLParser {
607
611
  children.push(new AST.TextNode(textContent))
608
612
  }
609
613
 
610
- const childElements = this.parseChildren()
614
+ const childElements = this.parseChildren(elemIndent)
611
615
  children.push(...childElements)
612
616
 
613
617
  return new AST.Element(tagName, htmlTag, attributes, children, false)
@@ -664,16 +668,13 @@ class HTMLParser {
664
668
  return attributes
665
669
  }
666
670
 
667
- parseChildren() {
671
+ parseChildren(parentIndent = -1) {
668
672
  const children = []
669
673
 
670
- this.skipNewlines()
671
- const baseIndent = this.currentIndent
672
-
673
674
  while (!this.match('EOF')) {
674
675
  this.skipNewlines()
675
676
 
676
- if (this.currentIndent <= baseIndent && children.length > 0) {
677
+ if (this.currentIndent <= parentIndent) {
677
678
  break
678
679
  }
679
680
 
@@ -807,12 +808,12 @@ class HTMLParser {
807
808
  'tete': 'head', 'tête': 'head', 'head': 'head', 'cabeza': 'head', 'голова': 'head', '头部': 'head', 'ヘッド': 'head',
808
809
  'corps': 'body', 'body': 'body', 'cuerpo': 'body', 'тело': 'body', '主体': 'body', 'ボディ': 'body',
809
810
  'titre': 'title', 'title': 'title', 'título': 'title', 'titulo': 'title', 'заголовок': 'title', '标题': 'title', 'タイトル': 'title',
810
- 'titre 1': 'h1', 'heading 1': 'h1', 'título 1': 'h1', 'titulo 1': 'h1', 'заголовок 1': 'h1', '标题 1': 'h1', '見出し 1': 'h1',
811
- 'titre 2': 'h2', 'heading 2': 'h2', 'título 2': 'h2', 'titulo 2': 'h2', 'заголовок 2': 'h2', '标题 2': 'h2', '見出し 2': 'h2',
812
- 'titre 3': 'h3', 'heading 3': 'h3', 'título 3': 'h3', 'titulo 3': 'h3', 'заголовок 3': 'h3', '标题 3': 'h3', '見出し 3': 'h3',
813
- 'titre 4': 'h4', 'heading 4': 'h4', 'título 4': 'h4', 'titulo 4': 'h4', 'заголовок 4': 'h4', '标题 4': 'h4', '見出し 4': 'h4',
814
- 'titre 5': 'h5', 'heading 5': 'h5', 'título 5': 'h5', 'titulo 5': 'h5', 'заголовок 5': 'h5', '标题 5': 'h5', '見出し 5': 'h5',
815
- 'titre 6': 'h6', 'heading 6': 'h6', 'título 6': 'h6', 'titulo 6': 'h6', 'заголовок 6': 'h6', '标题 6': 'h6', '見出し 6': 'h6',
811
+ 'titre 1': 'h1', 'titre1': 'h1', 'heading 1': 'h1', 'título 1': 'h1', 'titulo 1': 'h1', 'заголовок 1': 'h1', '标题 1': 'h1', '見出し 1': 'h1',
812
+ 'titre 2': 'h2', 'titre2': 'h2', 'heading 2': 'h2', 'título 2': 'h2', 'titulo 2': 'h2', 'заголовок 2': 'h2', '标题 2': 'h2', '見出し 2': 'h2',
813
+ 'titre 3': 'h3', 'titre3': 'h3', 'heading 3': 'h3', 'título 3': 'h3', 'titulo 3': 'h3', 'заголовок 3': 'h3', '标题 3': 'h3', '見出し 3': 'h3',
814
+ 'titre 4': 'h4', 'titre4': 'h4', 'heading 4': 'h4', 'título 4': 'h4', 'titulo 4': 'h4', 'заголовок 4': 'h4', '标题 4': 'h4', '見出し 4': 'h4',
815
+ 'titre 5': 'h5', 'titre5': 'h5', 'heading 5': 'h5', 'título 5': 'h5', 'titulo 5': 'h5', 'заголовок 5': 'h5', '标题 5': 'h5', '見出し 5': 'h5',
816
+ 'titre 6': 'h6', 'titre6': 'h6', 'heading 6': 'h6', 'título 6': 'h6', 'titulo 6': 'h6', 'заголовок 6': 'h6', '标题 6': 'h6', '見出し 6': 'h6',
816
817
  'paragraphe': 'p', 'paragraph': 'p', 'párrafo': 'p', 'parrafo': 'p', 'параграф': 'p', '段落': 'p',
817
818
  'division': 'div', 'div': 'div', 'bloque': 'div', 'блок': 'div', '块': 'div', 'ディブ': 'div',
818
819
  'etendue': 'span', 'étendue': 'span', 'span': 'span', 'extensión': 'span', 'extension': 'span', 'спан': 'span', '跨度': 'span', 'スパン': 'span',