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 +19 -7
- package/cli/ether.js +15 -28
- package/generators/html-generator.js +61 -19
- package/package.json +5 -5
- package/parsers/html-parser.js +14 -13
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
|
-
|
|
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|
|
|
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|
|
|
245
|
+
/\b(sélectionner|insérer|mettre à jour|supprimer|créer table|depuis|où)\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|
|
|
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.
|
|
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://
|
|
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 =
|
|
228
|
-
|
|
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
|
-
|
|
236
|
-
|
|
231
|
+
entete
|
|
232
|
+
titre1 "Bienvenue sur Ether"
|
|
237
233
|
paragraphe "Le langage intentionnel"
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
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
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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 (
|
|
544
|
-
|
|
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
|
-
|
|
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
|
+
"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": "
|
|
23
|
+
"author": "Steve",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/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-
|
|
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
|
+
}
|
package/parsers/html-parser.js
CHANGED
|
@@ -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 <=
|
|
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',
|