ether-code 0.6.3 → 0.6.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/compiler.js CHANGED
@@ -70,20 +70,52 @@ class EtherCompiler {
70
70
  this.parser = null
71
71
  this.resolvedIncludes = new Set()
72
72
 
73
- this.initGenerators()
74
73
  this.initParser()
74
+ this.initGenerators()
75
75
  }
76
76
 
77
77
  initGenerators() {
78
78
  const i18nDir = path.join(__dirname, '..', 'i18n')
79
79
 
80
+ const jsI18n = path.join(i18nDir, 'i18n-js.json')
81
+ if (fs.existsSync(jsI18n)) {
82
+ this.generators['js'] = new JSGenerator(jsI18n)
83
+ } else {
84
+ this.generators['js'] = new JSGenerator()
85
+ }
86
+ this.generators['javascript'] = this.generators['js']
87
+
88
+ const cssI18n = path.join(i18nDir, 'i18n-css.json')
89
+ if (fs.existsSync(cssI18n)) {
90
+ this.generators['css'] = new CSSGenerator(cssI18n)
91
+ } else {
92
+ this.generators['css'] = new CSSGenerator()
93
+ }
94
+
80
95
  for (const [target, GeneratorClass] of Object.entries(GENERATORS)) {
96
+ if (target === 'js' || target === 'javascript' || target === 'css') {
97
+ continue
98
+ }
99
+
81
100
  const i18nFile = path.join(i18nDir, `i18n-${this.normalizeTarget(target)}.json`)
82
101
 
83
- if (fs.existsSync(i18nFile)) {
84
- this.generators[target] = new GeneratorClass(i18nFile)
102
+ if (target === 'html') {
103
+ const options = {
104
+ parser: this.parser,
105
+ jsGenerator: this.generators['js'],
106
+ cssGenerator: this.generators['css']
107
+ }
108
+ if (fs.existsSync(i18nFile)) {
109
+ this.generators[target] = new GeneratorClass(i18nFile, options)
110
+ } else {
111
+ this.generators[target] = new GeneratorClass(null, options)
112
+ }
85
113
  } else {
86
- this.generators[target] = new GeneratorClass()
114
+ if (fs.existsSync(i18nFile)) {
115
+ this.generators[target] = new GeneratorClass(i18nFile)
116
+ } else {
117
+ this.generators[target] = new GeneratorClass()
118
+ }
87
119
  }
88
120
  }
89
121
  }
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.6.3'
9
+ const VERSION = '0.6.4'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
package/ether-parser.js CHANGED
@@ -17,16 +17,16 @@ class EtherParser {
17
17
  initCompoundTerms() {
18
18
  this.compoundOperators = {
19
19
  'strictement egal': '===',
20
- 'strictement égal': '===',
20
+ 'strictement égal': '===',
21
21
  'strictement different': '!==',
22
- 'strictement différent': '!==',
22
+ 'strictement différent': '!==',
23
23
  'superieur ou egal': '>=',
24
- 'supérieur ou égal': '>=',
24
+ 'supérieur ou égal': '>=',
25
25
  'inferieur ou egal': '<=',
26
- 'inférieur ou égal': '<=',
26
+ 'inférieur ou égal': '<=',
27
27
  'coalescence nulle': '??',
28
28
  'chainage optionnel': '?.',
29
- 'chaînage optionnel': '?.'
29
+ 'chaînage optionnel': '?.'
30
30
  }
31
31
 
32
32
  this.compoundKeywords = {
@@ -36,7 +36,7 @@ class EtherParser {
36
36
  'fonction asynchrone': 'async function',
37
37
  'fonction async': 'async function',
38
38
  'fonction generatrice': 'function*',
39
- 'fonction génératrice': 'function*',
39
+ 'fonction génératrice': 'function*',
40
40
  'variable globale': 'var'
41
41
  }
42
42
 
@@ -281,7 +281,7 @@ class EtherParser {
281
281
  expect(type) {
282
282
  const token = this.current()
283
283
  if (!token || token.type !== type) {
284
- throw new Error(`Attendu ${type}, reçu ${token ? token.type : 'EOF'}`)
284
+ throw new Error(`Attendu ${type}, reçu ${token ? token.type : 'EOF'}`)
285
285
  }
286
286
  return this.advance()
287
287
  }
@@ -695,7 +695,7 @@ class EtherParser {
695
695
  'alignement': ['texte'],
696
696
  'decoration': ['texte'],
697
697
  'transformation': ['texte'],
698
- 'ombre': ['texte', 'boite', 'boîte'],
698
+ 'ombre': ['texte', 'boite', 'boîte'],
699
699
  'espacement': ['lettres'],
700
700
  'bordure': ['haut', 'bas', 'gauche', 'droite', 'couleur', 'style', 'largeur', 'arrondi'],
701
701
  'couleur': ['bordure', 'fond', 'texte', 'remplissage'],
@@ -706,19 +706,19 @@ class EtherParser {
706
706
  'direction': ['flex'],
707
707
  'enveloppe': ['flex'],
708
708
  'justifier': ['contenu'],
709
- 'aligner': ['elements', 'éléments', 'contenu'],
709
+ 'aligner': ['elements', 'éléments', 'contenu'],
710
710
  'colonnes': ['grille'],
711
711
  'lignes': ['grille'],
712
712
  'espace': ['ligne', 'colonne', 'blanc'],
713
- 'débordement': ['x', 'y'],
713
+ 'débordement': ['x', 'y'],
714
714
  'debordement': ['x', 'y'],
715
- 'événements': ['pointeur'],
715
+ 'événements': ['pointeur'],
716
716
  'evenements': ['pointeur'],
717
- 'modèle': ['boite', 'boîte'],
718
- 'modele': ['boite', 'boîte'],
717
+ 'modèle': ['boite', 'boîte'],
718
+ 'modele': ['boite', 'boîte'],
719
719
  'filtre': ['fond'],
720
720
  'decoupe': ['fond'],
721
- 'découpe': ['fond'],
721
+ 'découpe': ['fond'],
722
722
  'origine': ['transformation', 'perspective'],
723
723
  'liste': ['style', 'type', 'position', 'image'],
724
724
  }
@@ -1565,21 +1565,251 @@ class EtherParser {
1565
1565
  })
1566
1566
  }
1567
1567
 
1568
+ const rawContentTags = ['script', 'style']
1569
+ const isRawContent = rawContentTags.includes(tag)
1570
+ const isScript = tag === 'script'
1571
+
1568
1572
  if (this.match(TokenType.INDENT)) {
1569
- while (!this.isAtEnd()) {
1570
- const current = this.current()
1571
-
1572
- if (current && current.type === TokenType.DEDENT) {
1573
- this.advance()
1574
- break
1573
+ if (isRawContent) {
1574
+ let rawText = []
1575
+ while (!this.isAtEnd()) {
1576
+ const current = this.current()
1577
+
1578
+ if (current && current.type === TokenType.DEDENT) {
1579
+ this.advance()
1580
+ break
1581
+ }
1582
+
1583
+ if (current && current.type === TokenType.NEWLINE) {
1584
+ rawText.push('\n')
1585
+ this.advance()
1586
+ continue
1587
+ }
1588
+
1589
+ if (current && current.type === TokenType.INDENT) {
1590
+ this.advance()
1591
+ continue
1592
+ }
1593
+
1594
+ if (current && current.type === TokenType.STRING) {
1595
+ if (isScript) {
1596
+ rawText.push("'" + current.value.replace(/'/g, "\\'") + "'")
1597
+ } else {
1598
+ rawText.push(current.value)
1599
+ }
1600
+ this.advance()
1601
+ continue
1602
+ }
1603
+
1604
+ if (current && current.type === TokenType.LPAREN) {
1605
+ rawText.push('(')
1606
+ this.advance()
1607
+ continue
1608
+ }
1609
+
1610
+ if (current && current.type === TokenType.RPAREN) {
1611
+ rawText.push(')')
1612
+ this.advance()
1613
+ continue
1614
+ }
1615
+
1616
+ if (current && current.type === TokenType.LBRACKET) {
1617
+ rawText.push('[')
1618
+ this.advance()
1619
+ continue
1620
+ }
1621
+
1622
+ if (current && current.type === TokenType.RBRACKET) {
1623
+ rawText.push(']')
1624
+ this.advance()
1625
+ continue
1626
+ }
1627
+
1628
+ if (current && current.type === TokenType.LBRACE) {
1629
+ rawText.push('{')
1630
+ this.advance()
1631
+ continue
1632
+ }
1633
+
1634
+ if (current && current.type === TokenType.RBRACE) {
1635
+ rawText.push('}')
1636
+ this.advance()
1637
+ continue
1638
+ }
1639
+
1640
+ if (current && current.type === TokenType.DOT) {
1641
+ rawText.push('.')
1642
+ this.advance()
1643
+ continue
1644
+ }
1645
+
1646
+ if (current && current.type === TokenType.COMMA) {
1647
+ rawText.push(',')
1648
+ this.advance()
1649
+ continue
1650
+ }
1651
+
1652
+ if (current && current.type === TokenType.COLON) {
1653
+ rawText.push(':')
1654
+ this.advance()
1655
+ continue
1656
+ }
1657
+
1658
+ if (current && current.type === TokenType.EQUALS) {
1659
+ rawText.push('=')
1660
+ this.advance()
1661
+ continue
1662
+ }
1663
+
1664
+ if (current && current.type === TokenType.DOUBLE_EQUALS) {
1665
+ rawText.push('==')
1666
+ this.advance()
1667
+ continue
1668
+ }
1669
+
1670
+ if (current && current.type === TokenType.NOT_EQUALS) {
1671
+ rawText.push('!=')
1672
+ this.advance()
1673
+ continue
1674
+ }
1675
+
1676
+ if (current && current.type === TokenType.BANG) {
1677
+ rawText.push('!')
1678
+ this.advance()
1679
+ continue
1680
+ }
1681
+
1682
+ if (current && current.type === TokenType.PLUS) {
1683
+ rawText.push(current.value)
1684
+ this.advance()
1685
+ continue
1686
+ }
1687
+
1688
+ if (current && current.type === TokenType.MINUS) {
1689
+ rawText.push(current.value)
1690
+ this.advance()
1691
+ continue
1692
+ }
1693
+
1694
+ if (current && current.type === TokenType.STAR) {
1695
+ rawText.push(current.value)
1696
+ this.advance()
1697
+ continue
1698
+ }
1699
+
1700
+ if (current && current.type === TokenType.SLASH) {
1701
+ rawText.push('/')
1702
+ this.advance()
1703
+ continue
1704
+ }
1705
+
1706
+ if (current && current.type === TokenType.PERCENT) {
1707
+ rawText.push('%')
1708
+ this.advance()
1709
+ continue
1710
+ }
1711
+
1712
+ if (current && current.type === TokenType.AMPERSAND) {
1713
+ rawText.push('&')
1714
+ this.advance()
1715
+ continue
1716
+ }
1717
+
1718
+ if (current && current.type === TokenType.DOUBLE_AMPERSAND) {
1719
+ rawText.push('&&')
1720
+ this.advance()
1721
+ continue
1722
+ }
1723
+
1724
+ if (current && current.type === TokenType.PIPE) {
1725
+ rawText.push('|')
1726
+ this.advance()
1727
+ continue
1728
+ }
1729
+
1730
+ if (current && current.type === TokenType.DOUBLE_PIPE) {
1731
+ rawText.push('||')
1732
+ this.advance()
1733
+ continue
1734
+ }
1735
+
1736
+ if (current && current.type === TokenType.LT) {
1737
+ rawText.push('<')
1738
+ this.advance()
1739
+ continue
1740
+ }
1741
+
1742
+ if (current && current.type === TokenType.GT) {
1743
+ rawText.push('>')
1744
+ this.advance()
1745
+ continue
1746
+ }
1747
+
1748
+ if (current && current.type === TokenType.LTE) {
1749
+ rawText.push('<=')
1750
+ this.advance()
1751
+ continue
1752
+ }
1753
+
1754
+ if (current && current.type === TokenType.GTE) {
1755
+ rawText.push('>=')
1756
+ this.advance()
1757
+ continue
1758
+ }
1759
+
1760
+ if (current && current.type === TokenType.QUESTION) {
1761
+ rawText.push('?')
1762
+ this.advance()
1763
+ continue
1764
+ }
1765
+
1766
+ if (current && current.type === TokenType.FAT_ARROW) {
1767
+ rawText.push('=>')
1768
+ this.advance()
1769
+ continue
1770
+ }
1771
+
1772
+ if (current && current.type === TokenType.ARROW) {
1773
+ rawText.push('->')
1774
+ this.advance()
1775
+ continue
1776
+ }
1777
+
1778
+ if (current && current.type === TokenType.SEMICOLON) {
1779
+ rawText.push(';')
1780
+ this.advance()
1781
+ continue
1782
+ }
1783
+
1784
+ if (current) {
1785
+ rawText.push(String(current.value))
1786
+ this.advance()
1787
+ }
1575
1788
  }
1576
1789
 
1577
- const child = this.parseHTMLNode()
1578
- if (child) {
1579
- children.push(child)
1790
+ const textContent = rawText.join('').trim()
1791
+ if (textContent) {
1792
+ children.push({
1793
+ type: 'Text',
1794
+ content: textContent
1795
+ })
1796
+ }
1797
+ } else {
1798
+ while (!this.isAtEnd()) {
1799
+ const current = this.current()
1800
+
1801
+ if (current && current.type === TokenType.DEDENT) {
1802
+ this.advance()
1803
+ break
1804
+ }
1805
+
1806
+ const child = this.parseHTMLNode()
1807
+ if (child) {
1808
+ children.push(child)
1809
+ }
1810
+
1811
+ this.skipNewlines()
1580
1812
  }
1581
-
1582
- this.skipNewlines()
1583
1813
  }
1584
1814
  }
1585
1815
 
@@ -1782,7 +2012,8 @@ class EtherParser {
1782
2012
  'zone': 'area'
1783
2013
  }
1784
2014
 
1785
- return translations[lower] || translations[withSpaces] || map[lower] || map[withSpaces] || tag
2015
+ const result = translations[lower] || translations[withSpaces] || map[lower] || map[withSpaces] || tag
2016
+ return result.replace(/^<!DOCTYPE\s+/i, '').replace(/^<|>$/g, '').trim() || 'div'
1786
2017
  }
1787
2018
 
1788
2019
  parseJS() {
@@ -2557,16 +2788,16 @@ class EtherParser {
2557
2788
  let left = this.parseComparison(lang)
2558
2789
 
2559
2790
  while (true) {
2560
- if (this.matchValue('strictement egal') || this.matchValue('strictement égal')) {
2791
+ if (this.matchValue('strictement egal') || this.matchValue('strictement égal')) {
2561
2792
  const right = this.parseComparison(lang)
2562
2793
  left = { type: 'BinaryExpression', operator: '===', left, right }
2563
- } else if (this.matchValue('strictement different') || this.matchValue('strictement différent')) {
2794
+ } else if (this.matchValue('strictement different') || this.matchValue('strictement différent')) {
2564
2795
  const right = this.parseComparison(lang)
2565
2796
  left = { type: 'BinaryExpression', operator: '!==', left, right }
2566
- } else if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale') || this.matchValue('égal')) {
2797
+ } else if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale') || this.matchValue('égal')) {
2567
2798
  const right = this.parseComparison(lang)
2568
2799
  left = { type: 'BinaryExpression', operator: '===', left, right }
2569
- } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different') || this.matchValue('différent')) {
2800
+ } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different') || this.matchValue('différent')) {
2570
2801
  const right = this.parseComparison(lang)
2571
2802
  left = { type: 'BinaryExpression', operator: '!==', left, right }
2572
2803
  } else {
@@ -2581,16 +2812,16 @@ class EtherParser {
2581
2812
  let left = this.parseAdditive(lang)
2582
2813
 
2583
2814
  while (true) {
2584
- if (this.matchValue('superieur ou egal') || this.matchValue('supérieur ou égal') || this.match(TokenType.GTE)) {
2815
+ if (this.matchValue('superieur ou egal') || this.matchValue('supérieur ou égal') || this.match(TokenType.GTE)) {
2585
2816
  const right = this.parseAdditive(lang)
2586
2817
  left = { type: 'BinaryExpression', operator: '>=', left, right }
2587
- } else if (this.matchValue('inferieur ou egal') || this.matchValue('inférieur ou égal') || this.match(TokenType.LTE)) {
2818
+ } else if (this.matchValue('inferieur ou egal') || this.matchValue('inférieur ou égal') || this.match(TokenType.LTE)) {
2588
2819
  const right = this.parseAdditive(lang)
2589
2820
  left = { type: 'BinaryExpression', operator: '<=', left, right }
2590
- } else if (this.match(TokenType.LT) || this.matchValue('inferieur') || this.matchValue('inférieur')) {
2821
+ } else if (this.match(TokenType.LT) || this.matchValue('inferieur') || this.matchValue('inférieur')) {
2591
2822
  const right = this.parseAdditive(lang)
2592
2823
  left = { type: 'BinaryExpression', operator: '<', left, right }
2593
- } else if (this.match(TokenType.GT) || this.matchValue('superieur') || this.matchValue('supérieur')) {
2824
+ } else if (this.match(TokenType.GT) || this.matchValue('superieur') || this.matchValue('supérieur')) {
2594
2825
  const right = this.parseAdditive(lang)
2595
2826
  left = { type: 'BinaryExpression', operator: '>', left, right }
2596
2827
  } else {
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs')
2
2
 
3
3
  class HTMLGenerator {
4
- constructor(i18nPath = null) {
4
+ constructor(i18nPath = null, options = {}) {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
@@ -15,6 +15,10 @@ class HTMLGenerator {
15
15
  this.multiWordTags = []
16
16
  this.multiWordAttrs = []
17
17
 
18
+ this.parser = options.parser || null
19
+ this.jsGenerator = options.jsGenerator || null
20
+ this.cssGenerator = options.cssGenerator || null
21
+
18
22
  if (i18nPath) {
19
23
  this.loadI18n(i18nPath)
20
24
  }
@@ -537,8 +541,18 @@ class HTMLGenerator {
537
541
  this.output = ''
538
542
  this.indent = 0
539
543
 
540
- if (ast.type === 'document' || ast.type === 'root') {
541
- for (const child of ast.children || []) {
544
+ if (ast.type === 'Document' || ast.type === 'document' || ast.type === 'root') {
545
+ const children = ast.children || []
546
+
547
+ if (children.length > 0) {
548
+ const firstChild = children[0]
549
+ const firstTag = (firstChild.tag || firstChild.tagName || '').toLowerCase()
550
+ if (firstTag === 'html' || firstTag === 'document') {
551
+ this.output += '<!DOCTYPE html>\n'
552
+ }
553
+ }
554
+
555
+ for (const child of children) {
542
556
  this.generateNode(child)
543
557
  }
544
558
  } else {
@@ -552,19 +566,24 @@ class HTMLGenerator {
552
566
  if (!node) return
553
567
 
554
568
  switch (node.type) {
569
+ case 'Doctype':
555
570
  case 'doctype':
556
571
  this.generateDoctype(node)
557
572
  break
573
+ case 'Element':
558
574
  case 'element':
559
575
  case 'tag':
560
576
  this.generateElement(node)
561
577
  break
578
+ case 'Text':
562
579
  case 'text':
563
580
  this.generateText(node)
564
581
  break
582
+ case 'Comment':
565
583
  case 'comment':
566
584
  this.generateComment(node)
567
585
  break
586
+ case 'Include':
568
587
  case 'include':
569
588
  this.generateInclude(node)
570
589
  break
@@ -609,8 +628,18 @@ class HTMLGenerator {
609
628
  const textContent = typeof children === 'string' ? children :
610
629
  (node.text || node.textContent || null)
611
630
 
631
+ const isRawContent = (tag.toLowerCase() === 'script' || tag.toLowerCase() === 'style')
632
+
612
633
  if (textContent && !hasChildren) {
613
- this.writeLine(`<${tag}${attributes}>${this.escapeHtml(textContent)}</${tag}>`)
634
+ if (isRawContent) {
635
+ this.writeLine(`<${tag}${attributes}>`)
636
+ this.indent++
637
+ this.writeLine(textContent)
638
+ this.indent--
639
+ this.writeLine(`</${tag}>`)
640
+ } else {
641
+ this.writeLine(`<${tag}${attributes}>${this.escapeHtml(textContent)}</${tag}>`)
642
+ }
614
643
  return
615
644
  }
616
645
 
@@ -622,11 +651,33 @@ class HTMLGenerator {
622
651
  this.writeLine(`<${tag}${attributes}>`)
623
652
  this.indent++
624
653
 
625
- for (const child of children) {
626
- if (typeof child === 'string') {
627
- this.writeLine(this.escapeHtml(child))
628
- } else {
629
- this.generateNode(child)
654
+ if (isRawContent) {
655
+ let rawContent = ''
656
+ for (const child of children) {
657
+ if (typeof child === 'string') {
658
+ rawContent += child
659
+ } else if (child.type === 'Text' || child.type === 'text') {
660
+ const content = child.content || child.text || child.value || ''
661
+ if (content.trim()) {
662
+ rawContent += content
663
+ }
664
+ }
665
+ }
666
+
667
+ if (rawContent.trim()) {
668
+ const generatedCode = this.compileEmbeddedCode(rawContent.trim(), tag.toLowerCase())
669
+ const lines = generatedCode.split('\n')
670
+ for (const line of lines) {
671
+ this.writeLine(line)
672
+ }
673
+ }
674
+ } else {
675
+ for (const child of children) {
676
+ if (typeof child === 'string') {
677
+ this.writeLine(this.escapeHtml(child))
678
+ } else {
679
+ this.generateNode(child)
680
+ }
630
681
  }
631
682
  }
632
683
 
@@ -953,6 +1004,44 @@ class HTMLGenerator {
953
1004
  }
954
1005
  }
955
1006
  }
1007
+
1008
+ compileEmbeddedCode(code, tagType) {
1009
+ const etherKeywords = [
1010
+ 'journal', 'afficher', 'variable', 'constante', 'fonction', 'si', 'sinon',
1011
+ 'pour', 'tant que', 'retourner', 'classe', 'methode', 'importer',
1012
+ 'fond', 'couleur', 'marge', 'bordure', 'police', 'largeur', 'hauteur',
1013
+ 'remplissage', 'alignement', 'affichage', 'position', 'flexbox', 'grille'
1014
+ ]
1015
+
1016
+ const lowerCode = code.toLowerCase()
1017
+ const hasEtherKeywords = etherKeywords.some(kw => lowerCode.includes(kw))
1018
+
1019
+ if (!hasEtherKeywords) {
1020
+ return code
1021
+ }
1022
+
1023
+ if (tagType === 'script' && this.parser && this.jsGenerator) {
1024
+ try {
1025
+ const ast = this.parser.parse(code, 'js')
1026
+ const generated = this.jsGenerator.generate(ast)
1027
+ return generated || code
1028
+ } catch (e) {
1029
+ return code
1030
+ }
1031
+ }
1032
+
1033
+ if (tagType === 'style' && this.parser && this.cssGenerator) {
1034
+ try {
1035
+ const ast = this.parser.parse(code, 'css')
1036
+ const generated = this.cssGenerator.generate(ast)
1037
+ return generated || code
1038
+ } catch (e) {
1039
+ return code
1040
+ }
1041
+ }
1042
+
1043
+ return code
1044
+ }
956
1045
  }
957
1046
 
958
1047
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {