@hyperfixi/vite-plugin 3.3.0 → 4.0.0

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/dist/index.cjs CHANGED
@@ -35,7 +35,6 @@ __export(index_exports, {
35
35
  REGIONS: () => REGIONS,
36
36
  SUPPORTED_LANGUAGES: () => SUPPORTED_LANGUAGES,
37
37
  clearCustomKeywords: () => clearCustomKeywords,
38
- clearSemanticParser: () => clearSemanticParser,
39
38
  computeUsageHash: () => computeUsageHash,
40
39
  default: () => index_default,
41
40
  descriptorToScanRule: () => descriptorToScanRule,
@@ -43,16 +42,76 @@ __export(index_exports, {
43
42
  getAllLanguageCodes: () => getAllLanguageCodes,
44
43
  getKeywordsForLanguage: () => getKeywordsForLanguage,
45
44
  getOptimalRegion: () => getOptimalRegion,
46
- hasSemanticParser: () => hasSemanticParser,
47
45
  hyperfixi: () => hyperfixi,
48
46
  isNonLatinLanguage: () => isNonLatinLanguage,
49
- registerCustomKeywords: () => registerCustomKeywords,
50
- setSemanticParser: () => setSemanticParser
47
+ registerCustomKeywords: () => registerCustomKeywords
51
48
  });
52
49
  module.exports = __toCommonJS(index_exports);
53
50
 
54
- // src/scanner.ts
55
- var import_bundle_generator = require("@hyperfixi/core/bundle-generator");
51
+ // src/engine-modules.ts
52
+ var engine = __toESM(require("@hyperfixi/engine"), 1);
53
+ var import_engine = require("@hyperfixi/engine");
54
+ function exportNameOf(mod) {
55
+ for (const [name, value] of Object.entries(engine)) {
56
+ if (value === mod) return name;
57
+ }
58
+ throw new Error("[hyperfixi] an engine module in `everything` is not a named export");
59
+ }
60
+ var ENGINE_MODULES = import_engine.everything.map((mod) => {
61
+ const grammar = (0, import_engine.createGrammar)();
62
+ mod(grammar);
63
+ return {
64
+ name: exportNameOf(mod),
65
+ // The template text line registers under a keyword no identifier can be
66
+ // spelled as (`#text`); it is not something a scan can find.
67
+ commands: Object.keys(grammar.commands).filter((k) => /^[a-z]+$/.test(k)),
68
+ features: Object.keys(grammar.features)
69
+ };
70
+ });
71
+ var byName = new Map(ENGINE_MODULES.map((m) => [m.name, m]));
72
+ var COMMAND_KEYWORDS = new Map(
73
+ ENGINE_MODULES.flatMap((m) => m.commands.map((k) => [k, m.name]))
74
+ );
75
+ var FEATURE_KEYWORDS = new Map(
76
+ ENGINE_MODULES.flatMap((m) => m.features.map((k) => [k, m.name]))
77
+ );
78
+ var NOT_SCANNED = /* @__PURE__ */ new Set(["on", "if", "live", "when", "bind"]);
79
+ var SCANNABLE_KEYWORDS = [
80
+ .../* @__PURE__ */ new Set([...COMMAND_KEYWORDS.keys(), ...FEATURE_KEYWORDS.keys()])
81
+ ].filter((k) => !NOT_SCANNED.has(k)).sort();
82
+ var BLOCK_MODULES = {
83
+ if: ["ifCommand"],
84
+ repeat: ["repeat", "loopControl"],
85
+ for: ["repeat", "loopControl"],
86
+ while: ["repeat", "loopControl"],
87
+ fetch: ["fetchCommand"]
88
+ };
89
+ var ALWAYS = ["on", "conversions"];
90
+ function resolveModules(usage) {
91
+ const wanted = new Set(ALWAYS);
92
+ const unknown = [];
93
+ for (const name of usage.commands) {
94
+ const mod = COMMAND_KEYWORDS.get(name) ?? FEATURE_KEYWORDS.get(name);
95
+ if (mod) wanted.add(mod);
96
+ else if (!(name in BLOCK_MODULES)) unknown.push(name);
97
+ }
98
+ for (const block of usage.blocks) {
99
+ for (const mod of BLOCK_MODULES[block] ?? []) wanted.add(mod);
100
+ }
101
+ if (usage.positional) wanted.add("expressionsExtra");
102
+ if (usage.reactivity) {
103
+ wanted.add("reactivity");
104
+ wanted.add("liveTemplates");
105
+ }
106
+ if (usage.construct) wanted.add("construct");
107
+ if (usage.cookies) wanted.add("cookies");
108
+ if (wanted.has("toggle")) wanted.add("toggleElement");
109
+ const modules = ENGINE_MODULES.filter((m) => wanted.has(m.name)).map((m) => m.name);
110
+ for (const name of wanted) {
111
+ if (!byName.has(name)) throw new Error(`[hyperfixi] no engine module named ${name}`);
112
+ }
113
+ return { modules, unknown: [...new Set(unknown)].sort() };
114
+ }
56
115
 
57
116
  // src/language-keywords.ts
58
117
  var SUPPORTED_LANGUAGES = [
@@ -1089,17 +1148,7 @@ var SSE_NS_PATTERN = /\bsse-[\w\-\p{L}]+\s*=\s*["']/u;
1089
1148
  var WS_NS_PATTERN = /\bws-[\w\-\p{L}]+\s*=\s*["']/u;
1090
1149
 
1091
1150
  // src/scanner.ts
1092
- var SCANNABLE_COMMANDS = [
1093
- .../* @__PURE__ */ new Set([...import_bundle_generator.AVAILABLE_COMMANDS, ...import_bundle_generator.FULL_RUNTIME_ONLY_COMMANDS])
1094
- ].filter(
1095
- (cmd) => /^[A-Za-z]+$/.test(cmd) && // `unless` is handled by the block detection below (it compiles to the
1096
- // lite-capable `if` block — flagging it as a full-runtime-only COMMAND
1097
- // would needlessly bump every unless-user to a full-runtime bundle).
1098
- // `bind` is handled by the dedicated reactivity detection below, which
1099
- // routes to the hx-v4 bundle (the tier that actually ships reactivity).
1100
- cmd !== "unless" && cmd !== "bind"
1101
- );
1102
- var COMMAND_PATTERN_SOURCE = `\\b(${SCANNABLE_COMMANDS.join("|")})\\b`;
1151
+ var COMMAND_PATTERN_SOURCE = `\\b(${SCANNABLE_KEYWORDS.join("|")})\\b`;
1103
1152
  var HTMX_REQUEST_PATTERN = /\b(hx-get|hx-post|hx-put|hx-patch|hx-delete)\s*=\s*["']([^"']+)["']/gi;
1104
1153
  var FIXI_ACTION_PATTERN = /\bfx-action\s*=\s*["']([^"']+)["']/gi;
1105
1154
  var FIXI_METHOD_PATTERN = /\bfx-method\s*=\s*["'](GET|POST|PUT|PATCH|DELETE)["']/gi;
@@ -1151,7 +1200,9 @@ var Scanner = class {
1151
1200
  positional: false,
1152
1201
  detectedLanguages: /* @__PURE__ */ new Set(),
1153
1202
  needsReactivity: false,
1154
- needsBindToProperty: false
1203
+ needsBindToProperty: false,
1204
+ needsConstruct: false,
1205
+ needsCookies: false
1155
1206
  };
1156
1207
  const attrPatterns = [
1157
1208
  /_\s*=\s*"([^"]+)"/g,
@@ -1219,9 +1270,11 @@ var Scanner = class {
1219
1270
  if (/\bfor\s+(each|every)\b/i.test(script)) usage.blocks.add("for");
1220
1271
  if (/\bwhile\b/.test(script)) usage.blocks.add("while");
1221
1272
  if (/\bfetch\b/.test(script)) usage.blocks.add("fetch");
1222
- if (/\b(first|last|next|previous|closest|parent)\b/.test(script)) {
1273
+ if (/\b(first|last|next|previous|closest|parent|random)\b/.test(script) || /\b(where|sorted\s+by|mapped\s+to|split\s+by|joined\s+by|some|beep!)/.test(script)) {
1223
1274
  usage.positional = true;
1224
1275
  }
1276
+ if (/\bnew\s+[A-Z$_][\w$.]*\s*\(/.test(script)) usage.needsConstruct = true;
1277
+ if (/\bcookies\b/.test(script)) usage.needsCookies = true;
1225
1278
  const languages = detectLanguages(script);
1226
1279
  for (const lang of languages) {
1227
1280
  usage.detectedLanguages.add(lang);
@@ -1416,7 +1469,8 @@ var Aggregator = class {
1416
1469
  const positionalEqual = existing.positional === usage.positional;
1417
1470
  const languagesEqual = this.setsEqual(existing.detectedLanguages, usage.detectedLanguages);
1418
1471
  const htmxEqual = this.htmxUsageEqual(existing.htmx, usage.htmx);
1419
- if (commandsEqual && blocksEqual && positionalEqual && languagesEqual && htmxEqual) {
1472
+ const flagsEqual = !!existing.needsReactivity === !!usage.needsReactivity && !!existing.needsBindToProperty === !!usage.needsBindToProperty && !!existing.needsConstruct === !!usage.needsConstruct && !!existing.needsCookies === !!usage.needsCookies;
1473
+ if (commandsEqual && blocksEqual && positionalEqual && languagesEqual && htmxEqual && flagsEqual) {
1420
1474
  return false;
1421
1475
  }
1422
1476
  }
@@ -1464,6 +1518,8 @@ var Aggregator = class {
1464
1518
  };
1465
1519
  let needsReactivity = false;
1466
1520
  let needsBindToProperty = false;
1521
+ let needsConstruct = false;
1522
+ let needsCookies = false;
1467
1523
  for (const usage of this.fileUsage.values()) {
1468
1524
  for (const cmd of usage.commands) commands.add(cmd);
1469
1525
  for (const block of usage.blocks) blocks.add(block);
@@ -1471,6 +1527,8 @@ var Aggregator = class {
1471
1527
  if (usage.positional) positional = true;
1472
1528
  if (usage.needsReactivity) needsReactivity = true;
1473
1529
  if (usage.needsBindToProperty) needsBindToProperty = true;
1530
+ if (usage.needsConstruct) needsConstruct = true;
1531
+ if (usage.needsCookies) needsCookies = true;
1474
1532
  if (usage.htmx) {
1475
1533
  if (usage.htmx.hasHtmxAttributes) htmx.hasHtmxAttributes = true;
1476
1534
  if (usage.htmx.hasFixiAttributes) htmx.hasFixiAttributes = true;
@@ -1499,6 +1557,8 @@ var Aggregator = class {
1499
1557
  htmx,
1500
1558
  needsReactivity,
1501
1559
  needsBindToProperty,
1560
+ needsConstruct,
1561
+ needsCookies,
1502
1562
  fileUsage: new Map(this.fileUsage)
1503
1563
  };
1504
1564
  return this.cachedUsage;
@@ -1553,9 +1613,9 @@ var Aggregator = class {
1553
1613
  htmxUsageEqual(a, b) {
1554
1614
  if (!a && !b) return true;
1555
1615
  if (!a || !b) return false;
1556
- return a.hasHtmxAttributes === b.hasHtmxAttributes && a.hasFixiAttributes === b.hasFixiAttributes && this.setsEqual(a.httpMethods, b.httpMethods) && this.setsEqual(a.swapStrategies, b.swapStrategies) && this.setsEqual(a.triggerModifiers, b.triggerModifiers) && this.setsEqual(a.urlManagement, b.urlManagement) && a.usesConfirm === b.usesConfirm && !!a.needsSwapTiming === !!b.needsSwapTiming && // htmx v4 / reactivity flags: these drive selection of the hx-v4 tier,
1557
- // so a change must count as "usage changed" or HMR serves a stale
1558
- // bundle. They were omitted when the v4 surface landed (#623).
1616
+ return a.hasHtmxAttributes === b.hasHtmxAttributes && a.hasFixiAttributes === b.hasFixiAttributes && this.setsEqual(a.httpMethods, b.httpMethods) && this.setsEqual(a.swapStrategies, b.swapStrategies) && this.setsEqual(a.triggerModifiers, b.triggerModifiers) && this.setsEqual(a.urlManagement, b.urlManagement) && a.usesConfirm === b.usesConfirm && !!a.needsSwapTiming === !!b.needsSwapTiming && // htmx / reactivity flags: these decide which engine modules the bundle
1617
+ // registers, so a change must count as "usage changed" or HMR serves a
1618
+ // stale bundle. They were omitted when the v4 surface landed (#623).
1559
1619
  !!a.needsHxLive === !!b.needsHxLive && !!a.needsSSE === !!b.needsSSE && !!a.needsWS === !!b.needsWS && !!a.needsBindToProperty === !!b.needsBindToProperty && !!a.needsReactivity === !!b.needsReactivity && this.arraysEqual(a.onHandlers, b.onHandlers);
1560
1620
  }
1561
1621
  arraysEqual(a, b) {
@@ -1565,225 +1625,7 @@ var Aggregator = class {
1565
1625
  }
1566
1626
  };
1567
1627
 
1568
- // src/generator.ts
1569
- var import_bundle_generator2 = require("@hyperfixi/core/bundle-generator");
1570
-
1571
1628
  // src/semantic-integration.ts
1572
- var MULTILINGUAL_COMMAND_ALIASES = {
1573
- ja: {
1574
- \u30C8\u30B0\u30EB: "toggle",
1575
- \u5207\u308A\u66FF\u3048: "toggle",
1576
- \u8FFD\u52A0: "add",
1577
- \u524A\u9664: "remove",
1578
- \u8868\u793A: "show",
1579
- \u96A0\u3059: "hide",
1580
- \u975E\u8868\u793A: "hide",
1581
- \u8A2D\u5B9A: "set",
1582
- \u30BB\u30C3\u30C8: "set",
1583
- \u5897\u52A0: "increment",
1584
- \u6E1B\u5C11: "decrement",
1585
- \u30ED\u30B0: "log",
1586
- \u51FA\u529B: "log"
1587
- },
1588
- ko: {
1589
- \uD1A0\uAE00: "toggle",
1590
- \uC804\uD658: "toggle",
1591
- \uCD94\uAC00: "add",
1592
- \uC81C\uAC70: "remove",
1593
- \uC0AD\uC81C: "remove",
1594
- \uD45C\uC2DC: "show",
1595
- \uC228\uAE30\uB2E4: "hide",
1596
- \uC124\uC815: "set",
1597
- \uC99D\uAC00: "increment",
1598
- \uAC10\uC18C: "decrement",
1599
- \uB85C\uADF8: "log"
1600
- },
1601
- zh: {
1602
- \u5207\u6362: "toggle",
1603
- \u6DFB\u52A0: "add",
1604
- \u79FB\u9664: "remove",
1605
- \u5220\u9664: "remove",
1606
- \u663E\u793A: "show",
1607
- \u9690\u85CF: "hide",
1608
- \u8BBE\u7F6E: "set",
1609
- \u8BBE\u5B9A: "set",
1610
- \u589E\u52A0: "increment",
1611
- \u51CF\u5C11: "decrement",
1612
- \u65E5\u5FD7: "log",
1613
- \u8BB0\u5F55: "log"
1614
- },
1615
- ar: {
1616
- \u0628\u062F\u0651\u0644: "toggle",
1617
- \u0628\u062F\u0644: "toggle",
1618
- \u0623\u0636\u0641: "add",
1619
- \u0627\u0636\u0641: "add",
1620
- \u0623\u0632\u0644: "remove",
1621
- \u0627\u0632\u0644: "remove",
1622
- \u0627\u062D\u0630\u0641: "remove",
1623
- \u0623\u0638\u0647\u0631: "show",
1624
- \u0627\u0638\u0647\u0631: "show",
1625
- \u0623\u062E\u0641\u0650: "hide",
1626
- \u0627\u062E\u0641: "hide",
1627
- \u0636\u0639: "set",
1628
- \u0627\u0636\u0639: "set",
1629
- \u0632\u0650\u062F: "increment",
1630
- \u0623\u0646\u0642\u0635: "decrement"
1631
- },
1632
- es: {
1633
- alternar: "toggle",
1634
- a\u00F1adir: "add",
1635
- agregar: "add",
1636
- quitar: "remove",
1637
- eliminar: "remove",
1638
- mostrar: "show",
1639
- ocultar: "hide",
1640
- esconder: "hide",
1641
- establecer: "set",
1642
- fijar: "set",
1643
- incrementar: "increment",
1644
- decrementar: "decrement"
1645
- },
1646
- pt: {
1647
- alternar: "toggle",
1648
- adicionar: "add",
1649
- remover: "remove",
1650
- mostrar: "show",
1651
- esconder: "hide",
1652
- ocultar: "hide",
1653
- definir: "set",
1654
- incrementar: "increment",
1655
- decrementar: "decrement"
1656
- },
1657
- fr: {
1658
- basculer: "toggle",
1659
- ajouter: "add",
1660
- supprimer: "remove",
1661
- retirer: "remove",
1662
- afficher: "show",
1663
- montrer: "show",
1664
- cacher: "hide",
1665
- masquer: "hide",
1666
- d\u00E9finir: "set",
1667
- incr\u00E9menter: "increment",
1668
- d\u00E9cr\u00E9menter: "decrement"
1669
- },
1670
- de: {
1671
- umschalten: "toggle",
1672
- hinzuf\u00FCgen: "add",
1673
- entfernen: "remove",
1674
- l\u00F6schen: "remove",
1675
- anzeigen: "show",
1676
- zeigen: "show",
1677
- verbergen: "hide",
1678
- verstecken: "hide",
1679
- setzen: "set",
1680
- festlegen: "set",
1681
- erh\u00F6hen: "increment",
1682
- verringern: "decrement"
1683
- },
1684
- tr: {
1685
- de\u011Fi\u015Ftir: "toggle",
1686
- de\u011Fistir: "toggle",
1687
- ekle: "add",
1688
- kald\u0131r: "remove",
1689
- kaldir: "remove",
1690
- sil: "remove",
1691
- g\u00F6ster: "show",
1692
- gizle: "hide",
1693
- sakla: "hide",
1694
- ayarla: "set",
1695
- belirle: "set",
1696
- artt\u0131r: "increment",
1697
- azalt: "decrement"
1698
- },
1699
- id: {
1700
- alih: "toggle",
1701
- beralih: "toggle",
1702
- tambah: "add",
1703
- hapus: "remove",
1704
- buang: "remove",
1705
- tampilkan: "show",
1706
- sembunyikan: "hide",
1707
- atur: "set",
1708
- tetapkan: "set",
1709
- tambahkan: "increment",
1710
- kurangi: "decrement"
1711
- },
1712
- sw: {
1713
- badilisha: "toggle",
1714
- ongeza: "add",
1715
- ondoa: "remove",
1716
- futa: "remove",
1717
- onyesha: "show",
1718
- ficha: "hide",
1719
- weka: "set",
1720
- sanidi: "set",
1721
- ongezea: "increment",
1722
- punguza: "decrement"
1723
- },
1724
- qu: {
1725
- tikray: "toggle",
1726
- yapay: "add",
1727
- qichuy: "remove",
1728
- pichay: "remove",
1729
- rikuchiy: "show",
1730
- pakay: "hide",
1731
- churay: "set",
1732
- pisiyachiy: "decrement"
1733
- }
1734
- };
1735
- function getMultilingualCommandAliases(languages) {
1736
- const aliases = {};
1737
- for (const lang of languages) {
1738
- const langAliases = MULTILINGUAL_COMMAND_ALIASES[lang];
1739
- if (langAliases) {
1740
- Object.assign(aliases, langAliases);
1741
- }
1742
- }
1743
- return aliases;
1744
- }
1745
- function generateMultilingualAliases(languages) {
1746
- const aliases = {};
1747
- for (const lang of languages) {
1748
- const langAliases = MULTILINGUAL_COMMAND_ALIASES[lang];
1749
- if (langAliases) {
1750
- Object.assign(aliases, langAliases);
1751
- }
1752
- }
1753
- if (Object.keys(aliases).length === 0) {
1754
- return `
1755
- // No multilingual aliases needed - just pass through
1756
- function preprocessMultilingual(code) { return code; }
1757
- `;
1758
- }
1759
- const aliasEntries = Object.entries(aliases).map(([key, val]) => ` '${key}': '${val}'`).join(",\n");
1760
- const escapedKeys = Object.keys(aliases).map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
1761
- return `
1762
- // Multilingual command aliases for HybridParser fallback
1763
- const MULTILINGUAL_ALIASES = {
1764
- ${aliasEntries}
1765
- };
1766
-
1767
- // Register multilingual aliases with the parser
1768
- addCommandAliases(MULTILINGUAL_ALIASES);
1769
-
1770
- // Preprocessing regex to translate multilingual keywords to English
1771
- // The HybridParser tokenizer only accepts ASCII identifiers, so we need
1772
- // to translate non-ASCII keywords before parsing.
1773
- const MULTILINGUAL_KEYWORD_REGEX = new RegExp('(^|\\\\s)(${escapedKeys})(?=\\\\s|\\\\.|$)', 'g');
1774
-
1775
- /**
1776
- * Preprocess multilingual code to replace non-ASCII keywords with English equivalents.
1777
- * This is needed because HybridParser's tokenizer only accepts ASCII identifiers.
1778
- */
1779
- function preprocessMultilingual(code) {
1780
- return code.replace(MULTILINGUAL_KEYWORD_REGEX, (match, prefix, keyword) => {
1781
- const english = MULTILINGUAL_ALIASES[keyword];
1782
- return english ? prefix + english : match;
1783
- });
1784
- }
1785
- `;
1786
- }
1787
1629
  function resolveSemanticConfig(options, detectedLanguages) {
1788
1630
  const grammarEnabled = options.grammar ?? false;
1789
1631
  let semanticEnabled = grammarEnabled;
@@ -1999,15 +1841,11 @@ function generateSemanticIntegrationCode(config) {
1999
1841
  const languages = [...config.languages].join("', '");
2000
1842
  let code = `
2001
1843
  // =============================================================================
2002
- // SEMANTIC PARSER INTEGRATION
1844
+ // MULTILINGUAL (semantic) \u2014 translated as read; the attribute stays as written
2003
1845
  // =============================================================================
2004
1846
 
2005
1847
  // Core infrastructure (no language data bundled)
2006
- import {
2007
- parseWithConfidence,
2008
- buildAST,
2009
- isLanguageSupported,
2010
- } from '@lokascript/semantic/core';
1848
+ import { parseWithConfidence, render, isLanguageRegistered } from '@lokascript/semantic/core';
2011
1849
 
2012
1850
  // Register required languages (self-registering side-effect imports)
2013
1851
  `;
@@ -2021,58 +1859,36 @@ import {
2021
1859
  import { translate } from '@lokascript/semantic/core';
2022
1860
  `;
2023
1861
  }
2024
- const aliasesCode = generateMultilingualAliases(config.languages);
2025
1862
  code += `
2026
1863
  const SUPPORTED_SEMANTIC_LANGUAGES = ['${languages}', 'en'];
2027
1864
  const SEMANTIC_CONFIDENCE_THRESHOLD = 0.7;
2028
1865
 
2029
- ${aliasesCode}
1866
+ /** The element's language: data-lang, data-hyperscript-lang, the lang cascade, the document's. */
1867
+ function resolveLanguage(elt) {
1868
+ const code =
1869
+ elt.getAttribute('data-lang') ||
1870
+ elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang') ||
1871
+ elt.closest?.('[lang]')?.getAttribute('lang') ||
1872
+ (typeof document !== 'undefined' ? document.documentElement?.lang : null);
1873
+ return code ? code.toLowerCase().split(/[-_]/)[0] : null;
1874
+ }
2030
1875
 
2031
1876
  /**
2032
- * Parse hyperscript using semantic parser with auto-language detection.
2033
- * Tries each supported language until one returns a high-confidence result.
2034
- * Falls back to HybridParser if none match.
2035
- *
2036
- * @param code - The hyperscript code to parse
2037
- * @param lang - Optional language hint. If provided, tries this language first.
1877
+ * The engine's source transform: the English for a non-English script, or null
1878
+ * to leave the script as written (English, an unregistered language, a parse
1879
+ * below the confidence threshold, or a render that changed nothing).
2038
1880
  */
2039
- function parseWithSemantic(code, lang = null) {
2040
- // Determine languages to try: specified lang first, then others
2041
- let languagesToTry = [...SUPPORTED_SEMANTIC_LANGUAGES];
2042
- if (lang && !languagesToTry.includes(lang)) {
2043
- languagesToTry.unshift(lang);
2044
- } else if (lang) {
2045
- // Move specified lang to front
2046
- languagesToTry = [lang, ...languagesToTry.filter(l => l !== lang)];
2047
- }
2048
-
2049
- // Try each language until one succeeds
2050
- for (const tryLang of languagesToTry) {
2051
- if (!isLanguageSupported(tryLang)) continue;
2052
- try {
2053
- const result = parseWithConfidence(code, tryLang);
2054
- if (result && result.confidence >= SEMANTIC_CONFIDENCE_THRESHOLD) {
2055
- // buildAST returns {ast, warnings} - extract the ast
2056
- const buildResult = buildAST(result.node);
2057
- const ast = buildResult.ast;
2058
-
2059
- // Semantic parser doesn't fully handle event handlers - fallback to HybridParser
2060
- // Event handlers like "on click toggle .active" produce incomplete AST
2061
- if (ast && ast.type === 'command' && ast.name === 'on') {
2062
- break; // Fallback to HybridParser
2063
- }
2064
-
2065
- return ast;
2066
- }
2067
- } catch (e) {
2068
- // Continue trying other languages
2069
- }
1881
+ function translateSource(src, elt) {
1882
+ const lang = elt ? resolveLanguage(elt) : null;
1883
+ if (!lang || lang === 'en' || !isLanguageRegistered(lang)) return null;
1884
+ try {
1885
+ const result = parseWithConfidence(src, lang);
1886
+ if (!result || !result.node || result.confidence < SEMANTIC_CONFIDENCE_THRESHOLD) return null;
1887
+ const english = render(result.node, 'en');
1888
+ return english && english !== src ? english : null;
1889
+ } catch {
1890
+ return null;
2070
1891
  }
2071
-
2072
- // Fallback to HybridParser for event handlers and unrecognized patterns
2073
- // Preprocess to translate non-ASCII keywords to English (tokenizer only accepts ASCII)
2074
- const preprocessedCode = preprocessMultilingual(code);
2075
- return new HybridParser(preprocessedCode).parse();
2076
1892
  }
2077
1893
  `;
2078
1894
  if (config.grammarEnabled) {
@@ -2082,9 +1898,7 @@ function parseWithSemantic(code, lang = null) {
2082
1898
  *
2083
1899
  * Returns the input UNCHANGED when it cannot be parsed in \`fromLang\`, or when
2084
1900
  * \`toLang\` is not one of the languages this bundle registered. The semantic
2085
- * translator throws in both cases; the i18n one this replaced never did \u2014 it
2086
- * degraded to word substitution, which turned unparseable input into plausible
2087
- * nonsense. Returning the source is the honest degradation.
1901
+ * translator throws in both cases; returning the source is the honest degradation.
2088
1902
  */
2089
1903
  function translateHyperscript(code, fromLang, toLang) {
2090
1904
  try {
@@ -2100,7 +1914,7 @@ function translateHyperscript(code, fromLang, toLang) {
2100
1914
  function getSemanticExports(config) {
2101
1915
  const exports2 = [];
2102
1916
  if (config.enabled) {
2103
- exports2.push("parseWithSemantic");
1917
+ exports2.push("translateSource");
2104
1918
  exports2.push("SUPPORTED_SEMANTIC_LANGUAGES");
2105
1919
  }
2106
1920
  if (config.grammarEnabled) {
@@ -2110,421 +1924,77 @@ function getSemanticExports(config) {
2110
1924
  }
2111
1925
 
2112
1926
  // src/generator.ts
2113
- function stripTypes(code) {
2114
- return code.replace(/\s+as\s+\w+<[^>]*(?:<[^>]*>[^>]*)*>/g, "").replace(/\s+as\s+\w+(?:\[\])?/g, "").replace(/:\s*\w+<[^>]*(?:<[^>]*>[^>]*)*>(?:\[\])?(?=\s*[=;,)\]])/g, "").replace(
2115
- /:\s*(?:string|number|boolean|any|unknown|void|never|null|undefined|object|symbol|bigint)(?=\s*[=;,)\]])/g,
2116
- ""
2117
- ).replace(/:\s*[A-Z]\w*(?:\[\])?(?=\s*[=;,)\]])/g, "").replace(/\((\w+):\s*(?:\w+|unknown|any)\)/g, "($1)").replace(/Promise<[^>]+>/g, "Promise").replace(/Array<[^>]+>/g, "Array").replace(/\):\s*\w+(?:<[^>]*>)?(?:\[\])?\s*(?=[{=])/g, ") ").replace(/\):\s*\w+(?:<[^>]*>)?\s*=>/g, ") =>").replace(
2118
- /:\s*(?:TransitionEvent|MouseEvent|KeyboardEvent|Event|Element|HTMLElement|Node)(?=\s*[=;,)\]])/g,
2119
- ""
2120
- ).replace(/(\w)!(?=[,;)\].\s])/g, "$1");
2121
- }
2122
- var COMMAND_IMPLEMENTATIONS = {};
2123
- var BLOCK_IMPLEMENTATIONS = {};
2124
- for (const [name, code] of Object.entries(import_bundle_generator2.COMMAND_IMPLEMENTATIONS)) {
2125
- COMMAND_IMPLEMENTATIONS[name] = stripTypes(code);
2126
- }
2127
- for (const [name, code] of Object.entries(import_bundle_generator2.BLOCK_IMPLEMENTATIONS)) {
2128
- BLOCK_IMPLEMENTATIONS[name] = stripTypes(code);
2129
- }
2130
- function generateBundleCode(config) {
1927
+ var ENGINE_API_NAMES = ["api", "boot", "register"];
1928
+ function generateBundleCode(spec) {
2131
1929
  const {
2132
1930
  name,
1931
+ modules,
2133
1932
  commands,
2134
- blocks = [],
2135
- htmxIntegration = false,
2136
- globalName = "hyperfixi",
2137
- positionalExpressions = false,
2138
- autoInit = true,
2139
- esModule = true,
2140
- parserType = "hybrid"
2141
- } = config;
2142
- const needsStyleHelpers = commands.some((cmd) => import_bundle_generator2.STYLE_COMMANDS.includes(cmd));
2143
- const needsElementArrayHelper = commands.some((cmd) => import_bundle_generator2.ELEMENT_ARRAY_COMMANDS.includes(cmd));
2144
- const needsMorphlex = commands.some((cmd) => import_bundle_generator2.MORPH_COMMANDS.includes(cmd));
2145
- const hasBlocks = blocks.length > 0;
2146
- const hasReturn = commands.includes("return");
2147
- const commandCases = commands.filter((cmd) => COMMAND_IMPLEMENTATIONS[cmd]).map((cmd) => COMMAND_IMPLEMENTATIONS[cmd]).join("\n");
2148
- const blockCases = blocks.filter((block) => BLOCK_IMPLEMENTATIONS[block]).map((block) => BLOCK_IMPLEMENTATIONS[block]).join("\n");
2149
- const parserTemplate = parserType === "lite" ? import_bundle_generator2.LITE_PARSER_TEMPLATE : import_bundle_generator2.HYBRID_PARSER_TEMPLATE;
2150
- const morphlexImport = needsMorphlex ? `import { morph as morphlexMorph, morphInner as morphlexMorphInner } from 'morphlex';
2151
-
1933
+ blocks,
1934
+ positional,
1935
+ reactivity,
1936
+ htmxIntegration,
1937
+ globalName,
1938
+ autoInit,
1939
+ esModule,
1940
+ semantic
1941
+ } = spec;
1942
+ const semanticCode = generateSemanticIntegrationCode(semantic);
1943
+ const semanticExports = getSemanticExports(semantic);
1944
+ const languages = semantic.enabled ? [...semantic.languages].sort() : [];
1945
+ const headerLines = [
1946
+ `Modules: ${modules.join(", ")}`,
1947
+ commands.length > 0 ? `Commands: ${commands.join(", ")}` : "",
1948
+ blocks.length > 0 ? `Blocks: ${blocks.join(", ")}` : "",
1949
+ positional ? "Expressions: positional / collection (expressionsExtra)" : "",
1950
+ reactivity ? "Reactivity: live / when / bind" : "",
1951
+ semantic.enabled ? `Multilingual: ${languages.join(", ") || "en"} (translated as read, through the engine's source transform)` : "",
1952
+ htmxIntegration ? "htmx: content swapped in is processed (htmx:load / htmx:afterSettle)" : ""
1953
+ ].filter(Boolean);
1954
+ const bundleInfo = JSON.stringify({
1955
+ generator: "@hyperfixi/vite-plugin",
1956
+ modules,
1957
+ commands,
1958
+ blocks,
1959
+ languages
1960
+ });
1961
+ const apiProps = [`bundle: ${bundleInfo}`, ...semanticExports].map((p) => ` ${p},`).join("\n");
1962
+ const installGlobal = globalName === "_hyperscript" ? "" : ` window.${globalName} = api;
1963
+ `;
1964
+ const htmxHooks = htmxIntegration ? `
1965
+ // htmx (any major) announces swapped-in content on these; hand it to the engine.
1966
+ for (const type of ['htmx:load', 'htmx:afterSettle']) {
1967
+ document.addEventListener(type, event => {
1968
+ const detail = event.detail;
1969
+ const target = (detail && (detail.elt || detail.target)) || event.target;
1970
+ if (target instanceof Element) api.processNode(target);
1971
+ });
1972
+ }
2152
1973
  ` : "";
2153
1974
  return `/**
2154
- * LokaScript ${name} Bundle (Auto-Generated)
1975
+ * hyperfixi ${name} bundle (auto-generated)
2155
1976
  *
2156
1977
  * Generated by: @hyperfixi/vite-plugin
2157
- * Parser: ${parserType}
2158
- * Commands: ${commands.join(", ")}${blocks.length > 0 ? `
2159
- * Blocks: ${blocks.join(", ")}` : ""}${positionalExpressions ? "\n * Positional expressions: enabled" : ""}${needsMorphlex ? "\n * Morphing: morphlex (~2.5KB)" : ""}
1978
+ * Engine: @hyperfixi/engine \u2014 the grammar is exactly the modules registered below.
1979
+ ${headerLines.map((l) => ` * ${l}`).join("\n")}
2160
1980
  *
2161
1981
  * DO NOT EDIT - This file is automatically regenerated.
2162
1982
  */
2163
1983
 
2164
- ${morphlexImport}// Embedded ${parserType} parser (no external dependencies)
2165
- ${parserTemplate}
2166
-
2167
- // Runtime state
2168
- const globalVars = new Map();
2169
- ${hasBlocks ? "const MAX_LOOP_ITERATIONS = 1000;" : ""}
2170
-
2171
- async function evaluate(node, ctx) {
2172
- switch (node.type) {
2173
- case 'literal': return node.value;
2174
-
2175
- case 'identifier':
2176
- if (node.value === 'me' || node.value === 'my') return ctx.me;
2177
- if (node.value === 'it') return ctx.it;
2178
- if (node.value === 'you') return ctx.you;
2179
- if (node.value === 'event') return ctx.event;
2180
- if (node.value === 'body') return document.body;
2181
- if (node.value === 'document') return document;
2182
- if (node.value === 'window') return window;
2183
- if (ctx.locals.has(node.value)) return ctx.locals.get(node.value);
2184
- if (node.value in ctx.me) return ctx.me[node.value];
2185
- return node.value;
2186
-
2187
- case 'contextReference':
2188
- // Semantic AST format for context references (me, you, it, etc.)
2189
- switch (node.contextType || node.name) {
2190
- case 'me': return ctx.me;
2191
- case 'it': return ctx.it;
2192
- case 'you': return ctx.you;
2193
- case 'event': return ctx.event;
2194
- case 'body': return document.body;
2195
- case 'document': return document;
2196
- case 'window': return window;
2197
- default: return ctx.me;
2198
- }
2199
-
2200
- case 'variable':
2201
- if (node.scope === 'local') return ctx.locals.get(node.name.slice(1));
2202
- const gName = node.name.slice(1);
2203
- if (globalVars.has(gName)) return globalVars.get(gName);
2204
- return window[node.name];
2205
-
2206
- case 'selector':
2207
- const elements = document.querySelectorAll(node.value);
2208
- return elements.length === 1 ? elements[0] : Array.from(elements);
2209
-
2210
- case 'binary':
2211
- return evaluateBinary(node, ctx);
2212
-
2213
- case 'possessive':
2214
- case 'member':
2215
- const obj = await evaluate(node.object, ctx);
2216
- if (obj == null) return undefined;
2217
- const prop = node.computed ? await evaluate(node.property, ctx) : node.property;
2218
- if (prop === 'values' && obj instanceof Element) {
2219
- if (obj instanceof HTMLFormElement) return new FormData(obj);
2220
- const fd = new FormData();
2221
- obj.querySelectorAll('input, select, textarea').forEach(input => {
2222
- const name = input.getAttribute('name');
2223
- if (name && 'value' in input) fd.append(name, input.value);
2224
- });
2225
- return fd;
2226
- }
2227
- return obj[prop];
2228
-
2229
- case 'call': {
2230
- let callContext = null;
2231
- let callee;
2232
-
2233
- if (node.callee.type === 'member' || node.callee.type === 'possessive') {
2234
- callContext = await evaluate(node.callee.object, ctx);
2235
- const p = node.callee.computed
2236
- ? await evaluate(node.callee.property, ctx)
2237
- : node.callee.property;
2238
- callee = callContext?.[p];
2239
- } else {
2240
- callee = await evaluate(node.callee, ctx);
2241
- }
2242
-
2243
- const args = await Promise.all(node.args.map(a => evaluate(a, ctx)));
2244
- if (typeof callee === 'function') return callee.apply(callContext, args);
2245
- return undefined;
2246
- }
2247
- ${positionalExpressions ? `
2248
- case 'positional':
2249
- return evaluatePositional(node, ctx);
2250
- ` : ""}
2251
- default: return undefined;
2252
- }
2253
- }
2254
-
2255
- async function evaluateBinary(node, ctx) {
2256
- if (node.operator === 'has') {
2257
- const left = await evaluate(node.left, ctx);
2258
- if (left instanceof Element && node.right.type === 'selector' && node.right.value.startsWith('.')) {
2259
- return left.classList.contains(node.right.value.slice(1));
2260
- }
2261
- return false;
2262
- }
2263
-
2264
- const left = await evaluate(node.left, ctx);
2265
- const right = await evaluate(node.right, ctx);
2266
-
2267
- switch (node.operator) {
2268
- case '+': return left + right;
2269
- case '-': return left - right;
2270
- case '*': return left * right;
2271
- case '/': return left / right;
2272
- case '%': return left % right;
2273
- case '==': case 'is': return left == right;
2274
- case '!=': case 'is not': return left != right;
2275
- case '<': return left < right;
2276
- case '>': return left > right;
2277
- case '<=': return left <= right;
2278
- case '>=': return left >= right;
2279
- case 'and': case '&&': return left && right;
2280
- case 'or': case '||': return left || right;
2281
- case 'contains': case 'includes':
2282
- if (typeof left === 'string') return left.includes(right);
2283
- if (Array.isArray(left)) return left.includes(right);
2284
- if (left instanceof Element) return left.contains(right);
2285
- return false;
2286
- case 'matches':
2287
- if (left instanceof Element) return left.matches(right);
2288
- if (typeof left === 'string') return new RegExp(right).test(left);
2289
- return false;
2290
- default: return undefined;
2291
- }
2292
- }
2293
- ${positionalExpressions ? `
2294
- function evaluatePositional(node, ctx) {
2295
- const target = node.target;
2296
- let elements = [];
2297
-
2298
- let selector = null;
2299
- if (target.type === 'selector') {
2300
- selector = target.value;
2301
- } else if (target.type === 'identifier') {
2302
- selector = target.value;
2303
- } else if (target.type === 'htmlSelector') {
2304
- selector = target.value;
2305
- }
2306
- if (selector) {
2307
- elements = Array.from(document.querySelectorAll(selector));
2308
- }
2309
-
2310
- switch (node.position) {
2311
- case 'first': return elements[0] || null;
2312
- case 'last': return elements[elements.length - 1] || null;
2313
- case 'next': return ctx.me.nextElementSibling;
2314
- case 'previous': return ctx.me.previousElementSibling;
2315
- case 'closest': return target.value ? ctx.me.closest(target.value) : null;
2316
- case 'parent': return ctx.me.parentElement;
2317
- default: return elements[0] || null;
2318
- }
2319
- }
2320
- ` : ""}
2321
- ${needsStyleHelpers ? `
2322
- const isStyleProp = (prop) => prop?.startsWith('*');
2323
- const getStyleName = (prop) => prop.substring(1);
2324
- const setStyleProp = (el, prop, value) => {
2325
- if (!isStyleProp(prop)) return false;
2326
- el.style.setProperty(getStyleName(prop), String(value));
2327
- return true;
2328
- };
2329
- ` : ""}
2330
-
2331
- ${needsElementArrayHelper ? `
2332
- const toElementArray = (val) => {
2333
- if (Array.isArray(val)) return val.filter(e => e instanceof Element);
2334
- if (val instanceof Element) return [val];
2335
- if (typeof val === 'string') return Array.from(document.querySelectorAll(val));
2336
- return [];
2337
- };
2338
- ` : ""}
2339
-
2340
- async function executeCommand(cmd, ctx) {
2341
- const getTarget = async () => {
2342
- // Support both HybridParser (cmd.target) and semantic AST (cmd.modifiers.on) formats
2343
- const target = cmd.target || cmd.modifiers?.on;
2344
- if (!target) return [ctx.me];
2345
- const t = await evaluate(target, ctx);
2346
- if (Array.isArray(t)) return t;
2347
- if (t instanceof Element) return [t];
2348
- if (typeof t === 'string') return Array.from(document.querySelectorAll(t));
2349
- return [ctx.me];
2350
- };
2351
-
2352
- const getClassName = (node) => {
2353
- if (!node) return '';
2354
- if (node.type === 'selector') return node.value.slice(1);
2355
- if (node.type === 'string' || node.type === 'literal') {
2356
- const val = node.value;
2357
- return typeof val === 'string' && val.startsWith('.') ? val.slice(1) : String(val);
2358
- }
2359
- if (node.type === 'identifier') return node.value;
2360
- return '';
2361
- };
2362
-
2363
- switch (cmd.name) {
2364
- ${commandCases}
2365
-
2366
- default:
2367
- console.warn(\`${name} bundle: Unknown command '\${cmd.name}'\`);
2368
- return null;
2369
- }
2370
- }
2371
- ${hasBlocks ? `
2372
- async function executeBlock(block, ctx) {
2373
- switch (block.type) {
2374
- ${blockCases}
2375
-
2376
- default:
2377
- console.warn(\`${name} bundle: Unknown block '\${block.type}'\`);
2378
- return null;
2379
- }
2380
- }
2381
- ` : ""}
2382
- async function executeSequence(nodes, ctx) {
2383
- let result;
2384
- for (const node of nodes) {
2385
- if (node.type === 'command') {
2386
- result = await executeCommand(node, ctx);
2387
- }${hasBlocks ? ` else if (['if', 'repeat', 'for', 'while', 'fetch'].includes(node.type)) {
2388
- result = await executeBlock(node, ctx);
2389
- }` : ""}
2390
- }
2391
- return result;
2392
- }
2393
- ${hasBlocks ? `
2394
- async function executeSequenceWithBlocks(nodes, ctx) {
2395
- try {
2396
- return await executeSequence(nodes, ctx);
2397
- } catch (e) {
2398
- if (e?.type === 'return') throw e;
2399
- throw e;
2400
- }
2401
- }
2402
- ` : ""}
2403
- async function executeAST(ast, me, event) {
2404
- const ctx = { me, event, locals: new Map() };
2405
-
2406
- if (ast.type === 'sequence') {
2407
- ${hasReturn || hasBlocks ? "try { return await executeSequence(ast.commands, ctx); } catch (e) { if (e?.type === 'return') return e.value; throw e; }" : "return executeSequence(ast.commands, ctx);"}
2408
- }
2409
-
2410
- if (ast.type === 'event') {
2411
- const eventNode = ast;
2412
- const eventName = eventNode.event;
2413
-
2414
- if (eventName === 'init') {
2415
- ${hasReturn || hasBlocks ? "try { await executeSequence(eventNode.body, ctx); } catch (e) { if (e?.type !== 'return') throw e; }" : "await executeSequence(eventNode.body, ctx);"}
2416
- return;
2417
- }
2418
-
2419
- const target = eventNode.filter ? await evaluate(eventNode.filter, ctx) : me;
2420
- const targetEl = target instanceof Element ? target : me;
2421
- const mods = eventNode.modifiers;
2422
-
2423
- let handler = async (e) => {
2424
- if (mods.prevent) e.preventDefault();
2425
- if (mods.stop) e.stopPropagation();
2426
-
2427
- const handlerCtx = {
2428
- me, event: e,
2429
- you: e.target instanceof Element ? e.target : undefined,
2430
- locals: new Map(),
2431
- };
2432
- ${hasReturn || hasBlocks ? "try { await executeSequence(eventNode.body, handlerCtx); } catch (err) { if (err?.type !== 'return') throw err; }" : "await executeSequence(eventNode.body, handlerCtx);"}
2433
- };
2434
-
2435
- if (mods.debounce) {
2436
- let timeout;
2437
- const orig = handler;
2438
- handler = async (e) => {
2439
- clearTimeout(timeout);
2440
- timeout = setTimeout(() => orig(e), mods.debounce);
2441
- };
2442
- }
2443
-
2444
- if (mods.throttle) {
2445
- let lastCall = 0;
2446
- const orig = handler;
2447
- handler = async (e) => {
2448
- const now = Date.now();
2449
- if (now - lastCall >= mods.throttle) {
2450
- lastCall = now;
2451
- await orig(e);
2452
- }
2453
- };
2454
- }
2455
-
2456
- targetEl.addEventListener(eventName, handler, { once: !!mods.once });
2457
- return;
2458
- }
2459
-
2460
- // Handle single command (not wrapped in event or sequence)
2461
- if (ast.type === 'command') {
2462
- return executeCommand(ast, ctx);
2463
- }
2464
-
2465
- return null;
2466
- }
2467
-
2468
- function processElement(el) {
2469
- const code = el.getAttribute('_');
2470
- if (!code) return;
2471
-
2472
- try {
2473
- const ast = ${parserType === "lite" ? "parseLite(code)" : "new HybridParser(code).parse()"};
2474
- executeAST(ast, el);
2475
- } catch (err) {
2476
- console.error('LokaScript ${name} error:', err, 'Code:', code);
2477
- }
2478
- }
2479
-
2480
- function processElements(root = document) {
2481
- const elements = root.querySelectorAll('[_]');
2482
- elements.forEach(processElement);
2483
- }
2484
-
2485
- const api = {
2486
- version: '1.0.0-${name.toLowerCase().replace(/\s+/g, "-")}',
2487
-
2488
- parse(code) {
2489
- return ${parserType === "lite" ? "parseLite(code)" : "new HybridParser(code).parse()"};
2490
- },
2491
-
2492
- async execute(code, element) {
2493
- const me = element || document.body;
2494
- const ast = ${parserType === "lite" ? "parseLite(code)" : "new HybridParser(code).parse()"};
2495
- return executeAST(ast, me);
2496
- },
2497
-
2498
- run: async (code, element) => api.execute(code, element),
2499
- eval: async (code, element) => api.execute(code, element),
2500
-
2501
- init: processElements,
2502
- process: processElements,
2503
-
2504
- commands: ${JSON.stringify(commands)},
2505
- ${blocks.length > 0 ? `blocks: ${JSON.stringify(blocks)},` : ""}
2506
- parserName: '${parserType}',
2507
- };
1984
+ import { ${[...ENGINE_API_NAMES, ...modules].join(", ")} } from '@hyperfixi/engine';
1985
+ ${semanticCode}
1986
+ register(${modules.join(", ")});
1987
+ ${semantic.enabled ? "api.addSourceTransform(translateSource);\n" : ""}
1988
+ Object.assign(api, {
1989
+ ${apiProps}
1990
+ });
2508
1991
  ${autoInit ? `
2509
1992
  if (typeof window !== 'undefined') {
2510
- window.${globalName} = api;
2511
-
2512
- if (document.readyState === 'loading') {
2513
- document.addEventListener('DOMContentLoaded', () => processElements());
2514
- } else {
2515
- processElements();
2516
- }
2517
- ${htmxIntegration ? `
2518
- document.addEventListener('htmx:afterSettle', (e) => {
2519
- const target = e.detail?.target;
2520
- if (target) processElements(target);
2521
- });
2522
- ` : ""}
2523
- }
2524
- ` : ""}
2525
- ${esModule ? `
1993
+ ${installGlobal} boot();
1994
+ ${htmxHooks}}
1995
+ ` : ""}${esModule ? `
2526
1996
  export default api;
2527
- export { api, processElements };
1997
+ export { api };
2528
1998
  ` : ""}`;
2529
1999
  }
2530
2000
  var Generator = class {
@@ -2535,148 +2005,70 @@ var Generator = class {
2535
2005
  * Generate bundle code from aggregated usage
2536
2006
  */
2537
2007
  generate(usage, options) {
2538
- const commands = [...usage.commands, ...options.extraCommands ?? []];
2539
- const blocks = [...usage.blocks, ...options.extraBlocks ?? []];
2008
+ const commands = [.../* @__PURE__ */ new Set([...usage.commands, ...options.extraCommands ?? []])];
2009
+ const blocks = [.../* @__PURE__ */ new Set([...usage.blocks, ...options.extraBlocks ?? []])];
2540
2010
  const positional = usage.positional || options.positional || false;
2541
- const semanticConfig = resolveSemanticConfig(options, usage.detectedLanguages);
2542
- if (commands.length === 0 && blocks.length === 0 && !positional && !semanticConfig.enabled) {
2011
+ const reactivity = Boolean(
2012
+ usage.needsReactivity || usage.needsBindToProperty || usage.htmx?.needsReactivity
2013
+ );
2014
+ const semantic = resolveSemanticConfig(options, usage.detectedLanguages);
2015
+ if (commands.length === 0 && blocks.length === 0 && !positional && !reactivity && !semantic.enabled) {
2543
2016
  return this.generateEmptyBundle(options);
2544
2017
  }
2545
- const needsV4Bundle = usage.needsReactivity || usage.htmx?.needsHxLive || usage.htmx?.needsSSE || usage.htmx?.needsWS;
2546
- if (needsV4Bundle) {
2547
- if (this.debug) {
2548
- const triggers = [];
2549
- if (usage.htmx?.needsHxLive) triggers.push("hx-live");
2550
- if (usage.htmx?.needsSSE) triggers.push("sse-connect/sse-swap");
2551
- if (usage.htmx?.needsWS) triggers.push("ws-connect/ws-send");
2552
- if (usage.needsBindToProperty) triggers.push("bind-to-property");
2553
- else if (usage.needsReactivity) triggers.push("live/when/bind in _=");
2018
+ const moduleUsage = {
2019
+ commands,
2020
+ blocks,
2021
+ positional,
2022
+ reactivity,
2023
+ construct: usage.needsConstruct,
2024
+ cookies: usage.needsCookies
2025
+ };
2026
+ const { modules, unknown } = resolveModules(moduleUsage);
2027
+ if (this.debug) {
2028
+ if (unknown.length > 0) {
2554
2029
  console.log(
2555
- `[hyperfixi] Detected htmx v4 features: ${triggers.join(", ")}
2556
- Using the hx-v4 premade bundle (auto-installs reactivity + SSE/WS).`
2030
+ `[hyperfixi] Scanned names with no engine module (a form the engine dropped, or a false positive): ${unknown.join(", ")}`
2557
2031
  );
2558
- const onlyReactivity = usage.needsReactivity && !usage.htmx?.needsHxLive && !usage.htmx?.needsSSE && !usage.htmx?.needsWS && !usage.htmx?.hasHtmxAttributes;
2559
- if (onlyReactivity) {
2560
- console.log(
2561
- ` Note: this project uses reactivity only (no hx-*/sse-*/ws-* attributes). A reactivity-only fallback is parked pending size feedback.`
2562
- );
2563
- }
2564
2032
  }
2565
- return this.generateDevFallback("hx-v4");
2566
- }
2567
- const unsupportedCommands = this.getUnsupportedCommands(commands);
2568
- if (unsupportedCommands.length > 0) {
2569
- if (this.debug) {
2033
+ if (usage.htmx?.needsHxLive || usage.htmx?.needsSSE || usage.htmx?.needsWS) {
2570
2034
  console.log(
2571
- `[hyperfixi] Commands detected that require full runtime: ${unsupportedCommands.join(", ")}
2572
- Falling back to full browser bundle for complete functionality.`
2035
+ "[hyperfixi] hx-live / sse-* / ws-* attributes seen: these are htmx 4 features on the engine (load htmx 4 beside this bundle; a hyperscript-bodied hx-live is a `live \u2026 end` block)."
2573
2036
  );
2574
2037
  }
2575
- return this.generateDevFallback("full");
2576
- }
2577
- const parserType = (0, import_bundle_generator2.canUseLiteParser)(commands, blocks, positional) ? "lite" : "hybrid";
2578
- const config = {
2579
- name: options.bundleName ?? "ViteAutoGenerated",
2580
- commands,
2581
- blocks,
2582
- positionalExpressions: positional,
2583
- htmxIntegration: options.htmx ?? usage.htmx?.hasHtmxAttributes ?? false,
2584
- globalName: options.globalName ?? "hyperfixi",
2585
- parserType,
2586
- autoInit: true,
2587
- esModule: true
2588
- };
2589
- if (this.debug) {
2590
2038
  const bundleInfo = {
2039
+ modules,
2591
2040
  commands,
2592
2041
  blocks,
2593
2042
  positional,
2594
- htmx: config.htmxIntegration,
2595
- semantic: semanticConfig.enabled,
2596
- parserType
2043
+ reactivity
2597
2044
  };
2598
- if (semanticConfig.enabled && semanticConfig.bundleType) {
2599
- const sizeInfo = getSemanticBundleSize(semanticConfig.bundleType);
2600
- bundleInfo.semanticBundle = semanticConfig.bundleType;
2045
+ if (semantic.enabled && semantic.bundleType) {
2046
+ const sizeInfo = getSemanticBundleSize(semantic.bundleType);
2047
+ bundleInfo.semanticBundle = semantic.bundleType;
2601
2048
  bundleInfo.semanticSize = sizeInfo.gzip;
2602
- bundleInfo.languages = [...semanticConfig.languages];
2603
- bundleInfo.grammar = semanticConfig.grammarEnabled;
2049
+ bundleInfo.languages = [...semantic.languages];
2050
+ bundleInfo.grammar = semantic.grammarEnabled;
2604
2051
  }
2605
2052
  console.log("[hyperfixi] Generating bundle:", bundleInfo);
2606
- if (semanticConfig.bundleType === "all" || semanticConfig.bundleType === "priority") {
2053
+ if (semantic.bundleType === "all" || semantic.bundleType === "priority") {
2607
2054
  console.log(
2608
- `[hyperfixi] Note: Using '${semanticConfig.bundleType}' semantic bundle (${getSemanticBundleSize(semanticConfig.bundleType).gzip}). Consider using 'region' option to select a smaller regional bundle if all languages aren't needed.`
2055
+ `[hyperfixi] Note: Using '${semantic.bundleType}' semantic bundle (${getSemanticBundleSize(semantic.bundleType).gzip}). Consider using 'region' option to select a smaller regional bundle if all languages aren't needed.`
2609
2056
  );
2610
2057
  }
2611
2058
  }
2612
- let bundleCode = generateBundleCode(config);
2613
- if (semanticConfig.enabled) {
2614
- bundleCode = this.addSemanticIntegration(bundleCode, semanticConfig);
2615
- }
2616
- return bundleCode;
2617
- }
2618
- /**
2619
- * Add semantic integration code to the bundle
2620
- */
2621
- addSemanticIntegration(bundleCode, config) {
2622
- const semanticCode = generateSemanticIntegrationCode(config);
2623
- const semanticExports = getSemanticExports(config);
2624
- const parserImportEnd = bundleCode.indexOf("// Runtime state");
2625
- if (parserImportEnd === -1) {
2626
- bundleCode = semanticCode + "\n" + bundleCode;
2627
- } else {
2628
- bundleCode = bundleCode.slice(0, parserImportEnd) + semanticCode + "\n\n" + bundleCode.slice(parserImportEnd);
2629
- }
2630
- bundleCode = bundleCode.replace(
2631
- /function processElement\(el\) \{\s*const code = el\.getAttribute\('_'\);\s*if \(!code\) return;\s*try \{\s*const ast = (?:new HybridParser\(code\)\.parse\(\)|parseLite\(code\));/g,
2632
- `function processElement(el) {
2633
- const code = el.getAttribute('_');
2634
- if (!code) return;
2635
-
2636
- try {
2637
- const ast = parseWithSemantic(code);`
2638
- );
2639
- bundleCode = bundleCode.replace(
2640
- /parse\(code\) \{\s*return (?:new HybridParser\(code\)\.parse\(\)|parseLite\(code\));/g,
2641
- `parse(code, lang = null) {
2642
- return parseWithSemantic(code, lang);`
2643
- );
2644
- bundleCode = bundleCode.replace(
2645
- /async execute\(code, element\) \{\s*const me = element \|\| document\.body;\s*const ast = (?:new HybridParser\(code\)\.parse\(\)|parseLite\(code\));/g,
2646
- `async execute(code, element, lang = null) {
2647
- const me = element || document.body;
2648
- const ast = parseWithSemantic(code, lang);`
2649
- );
2650
- if (semanticExports.length > 0) {
2651
- const apiExportsMarkerRegex = /parserName: '(?:lite|hybrid)',/;
2652
- const additionalApiProps = semanticExports.map((exp) => ` ${exp},`).join("\n");
2653
- bundleCode = bundleCode.replace(
2654
- apiExportsMarkerRegex,
2655
- `parserName: 'semantic',
2656
- ${additionalApiProps}`
2657
- );
2658
- }
2659
- return bundleCode;
2660
- }
2661
- /**
2662
- * Get commands that require the full runtime and are not available in generated bundles
2663
- */
2664
- getUnsupportedCommands(commands) {
2665
- const unsupported = [];
2666
- for (const cmd of commands) {
2667
- if (import_bundle_generator2.FULL_RUNTIME_ONLY_COMMANDS.includes(cmd)) {
2668
- unsupported.push(cmd);
2669
- } else if (!(0, import_bundle_generator2.isAvailableCommand)(cmd) && !COMMAND_IMPLEMENTATIONS[cmd]) {
2670
- unsupported.push(cmd);
2671
- }
2672
- }
2673
- return unsupported;
2674
- }
2675
- /**
2676
- * Check if any commands require full runtime and should trigger fallback
2677
- */
2678
- requiresFullRuntime(commands) {
2679
- return this.getUnsupportedCommands(commands).length > 0;
2059
+ return generateBundleCode({
2060
+ name: options.bundleName ?? "ViteAutoGenerated",
2061
+ modules,
2062
+ commands,
2063
+ blocks,
2064
+ positional,
2065
+ reactivity,
2066
+ htmxIntegration: options.htmx ?? usage.htmx?.hasHtmxAttributes ?? false,
2067
+ globalName: options.globalName ?? "hyperfixi",
2068
+ autoInit: true,
2069
+ esModule: true,
2070
+ semantic
2071
+ });
2680
2072
  }
2681
2073
  /**
2682
2074
  * Generate an empty bundle when no hyperscript is detected
@@ -2684,7 +2076,7 @@ ${additionalApiProps}`
2684
2076
  generateEmptyBundle(options) {
2685
2077
  const globalName = options.globalName ?? "hyperfixi";
2686
2078
  return `/**
2687
- * LokaScript Empty Bundle (Auto-Generated)
2079
+ * hyperfixi Empty Bundle (Auto-Generated)
2688
2080
  *
2689
2081
  * No hyperscript usage detected. This bundle provides a minimal API.
2690
2082
  * Add hyperscript attributes (_="...") to your HTML to enable features.
@@ -2694,28 +2086,24 @@ ${additionalApiProps}`
2694
2086
 
2695
2087
  const api = {
2696
2088
  version: '1.0.0-empty',
2697
- commands: [],
2698
- parserName: 'none',
2089
+ bundle: { generator: '@hyperfixi/vite-plugin', modules: [], commands: [], blocks: [], languages: [] },
2699
2090
 
2700
2091
  parse() {
2701
- console.warn('LokaScript: No parser loaded. Add hyperscript to your HTML to enable parsing.');
2702
- return { type: 'empty' };
2092
+ console.warn('hyperfixi: No engine loaded. Add hyperscript to your HTML to enable parsing.');
2093
+ return { errors: [] };
2703
2094
  },
2704
2095
 
2705
- async execute(code, element) {
2706
- console.warn('LokaScript: No commands loaded. Detected commands will be automatically included.');
2096
+ async evaluate(code, element) {
2097
+ console.warn('hyperfixi: No modules loaded. Detected commands will be automatically included.');
2707
2098
  return null;
2708
2099
  },
2709
2100
 
2710
- run: async (code, element) => api.execute(code, element),
2711
- eval: async (code, element) => api.execute(code, element),
2712
-
2713
- init() {},
2101
+ processNode() {},
2714
2102
  process() {},
2715
2103
  };
2716
2104
 
2717
2105
  if (typeof window !== 'undefined') {
2718
- (window).${globalName} = api;
2106
+ window.${globalName} = api;
2719
2107
  }
2720
2108
 
2721
2109
  export default api;
@@ -2723,947 +2111,40 @@ export { api };
2723
2111
  `;
2724
2112
  }
2725
2113
  /**
2726
- * Generate a development fallback bundle.
2114
+ * Generate a development fallback bundle: the engine with every module.
2727
2115
  *
2728
- * `hx-v4` re-exports the htmx v4 surface bundle: full runtime +
2729
- * htmx-compat + reactivity (hx-live, bind, when) + SSE + WS, all
2730
- * auto-installed. The vite plugin selects this when scanner detects
2731
- * any of `hx-live`, `sse-connect`/`sse-swap`, `ws-connect`/`ws-send`,
2732
- * or `bind ... to <expr>.<prop>` in `_=` bodies — the minimal
2733
- * generator can't satisfy those features (the slim runtime lacks the
2734
- * reactive notify chain and the SSE/WS modules). Larger artifact but
2735
- * correct end-to-end.
2116
+ * One shape serves every former fallback (`hybrid-complete`, `full`, `hx-v4`):
2117
+ * the engine's `everything` is 34 KB gzipped and carries the reactive features
2118
+ * (`live`, `when`, `bind`) the hx-v4 fallback existed for. The argument is kept
2119
+ * for the option's type; its value no longer changes the output.
2736
2120
  */
2737
- generateDevFallback(fallback) {
2738
- const bundle = fallback === "full" ? "@hyperfixi/core/browser" : fallback === "hx-v4" ? "@hyperfixi/core/browser/hybrid-hx-v4" : "@hyperfixi/core/browser/hybrid-complete";
2121
+ generateDevFallback(_fallback = "everything") {
2739
2122
  return `/**
2740
- * LokaScript Dev Fallback Bundle
2123
+ * hyperfixi Dev Fallback Bundle
2741
2124
  *
2742
- * Using pre-built ${fallback} bundle for faster development.
2743
- * Production builds will generate minimal bundles.
2125
+ * The engine with every module, for faster development rebuilds.
2126
+ * Production builds register only the modules the project uses.
2744
2127
  *
2745
2128
  * Generated by: @hyperfixi/vite-plugin
2746
2129
  */
2747
2130
 
2748
- export * from '${bundle}';
2749
- export { default } from '${bundle}';
2750
- `;
2751
- }
2752
- };
2131
+ import { api, boot, register, everything } from '@hyperfixi/engine';
2753
2132
 
2754
- // src/compiler.ts
2755
- var import_parser_core = require("@hyperfixi/core/parser/hybrid/parser-core");
2756
- var semanticParse = null;
2757
- var supportsLanguage = null;
2758
- var buildASTFn = null;
2759
- function setSemanticParser(parse, supports, buildAST) {
2760
- semanticParse = parse;
2761
- supportsLanguage = supports;
2762
- buildASTFn = buildAST;
2763
- }
2764
- function clearSemanticParser() {
2765
- semanticParse = null;
2766
- supportsLanguage = null;
2767
- buildASTFn = null;
2768
- }
2769
- function hasSemanticParser() {
2770
- return semanticParse !== null && supportsLanguage !== null && buildASTFn !== null;
2771
- }
2772
- var SEMANTIC_CONFIDENCE_THRESHOLD = 0.7;
2773
- var multilingualAliases = {};
2774
- var multilingualRegex = null;
2775
- function setMultilingualAliases(aliases) {
2776
- multilingualAliases = { ...aliases };
2777
- if (Object.keys(aliases).length > 0) {
2778
- const escapedKeys = Object.keys(aliases).map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2779
- multilingualRegex = new RegExp(`(^|\\s)(${escapedKeys})(?=\\s|\\.|$)`, "g");
2780
- } else {
2781
- multilingualRegex = null;
2782
- }
2783
- }
2784
- function preprocessMultilingual(code) {
2785
- if (!multilingualRegex) return code;
2786
- return code.replace(multilingualRegex, (match, prefix, keyword) => {
2787
- const english = multilingualAliases[keyword];
2788
- return english ? prefix + english : match;
2789
- });
2790
- }
2791
- function sanitizeClassName(name) {
2792
- if (!/^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
2793
- return null;
2794
- }
2795
- return name;
2796
- }
2797
- function sanitizeSelector(selector) {
2798
- return selector.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\0/g, "");
2799
- }
2800
- function hashScript(script) {
2801
- let hash = 5381;
2802
- for (let i = 0; i < script.length; i++) {
2803
- hash = (hash << 5) + hash ^ script.charCodeAt(i);
2804
- }
2805
- return Math.abs(hash).toString(36).slice(0, 4);
2806
- }
2807
- var usedIds = /* @__PURE__ */ new Set();
2808
- function resetCompiler() {
2809
- usedIds.clear();
2810
- }
2811
- function generateHandlerId(event, command, script) {
2812
- const hash = hashScript(script);
2813
- let id = `${event}_${command}_${hash}`;
2814
- let suffix = 0;
2815
- while (usedIds.has(id)) {
2816
- id = `${event}_${command}_${hash}${suffix++}`;
2817
- }
2818
- usedIds.add(id);
2819
- return id;
2820
- }
2821
- function compile(script, options = {}) {
2822
- const { language = "en", debug = false } = options;
2823
- try {
2824
- let ast;
2825
- if (language !== "en" && semanticParse && supportsLanguage && buildASTFn) {
2826
- if (supportsLanguage(language)) {
2827
- const result = semanticParse(script, language);
2828
- if (result.node && result.confidence >= SEMANTIC_CONFIDENCE_THRESHOLD) {
2829
- const { ast: builtAST, warnings } = buildASTFn(result.node);
2830
- if (debug && warnings.length > 0) {
2831
- console.log(`[hyperfixi] Semantic AST warnings for "${script}":`, warnings);
2832
- }
2833
- ast = builtAST;
2834
- if (debug) {
2835
- console.log(
2836
- `[hyperfixi] Compiled ${language}: "${script}" (confidence: ${result.confidence.toFixed(2)})`
2837
- );
2838
- }
2839
- } else {
2840
- if (debug) {
2841
- const reason = result.error || `low confidence (${result.confidence.toFixed(2)})`;
2842
- console.log(`[hyperfixi] Semantic parse failed for "${script}": ${reason}`);
2843
- }
2844
- return null;
2845
- }
2846
- } else {
2847
- if (debug) {
2848
- console.log(`[hyperfixi] Language "${language}" not supported by semantic parser`);
2849
- }
2850
- return null;
2851
- }
2852
- } else if (language !== "en") {
2853
- if (debug) {
2854
- console.log(
2855
- `[hyperfixi] Semantic parser not available for "${language}". Install @lokascript/semantic and call setSemanticParser().`
2856
- );
2857
- }
2858
- return null;
2859
- } else {
2860
- const preprocessedScript = preprocessMultilingual(script);
2861
- const parser = new import_parser_core.HybridParser(preprocessedScript);
2862
- ast = parser.parse();
2863
- }
2864
- return compileAST(ast, script);
2865
- } catch (error) {
2866
- if (debug) {
2867
- console.warn(`[hyperfixi] Failed to compile: ${script}`, error);
2868
- }
2869
- return null;
2870
- }
2871
- }
2872
- function extractPrimaryCommand(body) {
2873
- if (body.length === 0) return "handler";
2874
- const first = body[0];
2875
- if (first.type === "command") {
2876
- return first.name || "cmd";
2877
- }
2878
- return first.type || "handler";
2879
- }
2880
- function compileAST(ast, original) {
2881
- if (ast.type !== "event") {
2882
- return null;
2883
- }
2884
- const eventNode = ast;
2885
- const event = eventNode.event;
2886
- const modifiers = eventNode.modifiers || {};
2887
- const body = eventNode.body || [];
2888
- const { code, needsEvaluator, needsLocals, needsGlobals } = compileBody(body);
2889
- if (code === null || code === "") {
2890
- return null;
2891
- }
2892
- const command = extractPrimaryCommand(body);
2893
- const id = generateHandlerId(event, command, original);
2894
- return {
2895
- id,
2896
- event,
2897
- modifiers: {
2898
- prevent: modifiers.prevent,
2899
- stop: modifiers.stop,
2900
- once: modifiers.once,
2901
- debounce: modifiers.debounce,
2902
- throttle: modifiers.throttle
2903
- },
2904
- code,
2905
- needsEvaluator: needsEvaluator || needsLocals || needsGlobals,
2906
- original
2907
- };
2908
- }
2909
- function compileBody(nodes) {
2910
- const statements = [];
2911
- let needsEvaluator = false;
2912
- let needsLocals = false;
2913
- let needsGlobals = false;
2914
- for (const node of nodes) {
2915
- const result = compileNode(node);
2916
- if (result === null) {
2917
- return { code: null, needsEvaluator: false, needsLocals: false, needsGlobals: false };
2918
- }
2919
- statements.push(result.code);
2920
- needsEvaluator = needsEvaluator || result.needsEvaluator;
2921
- needsLocals = needsLocals || result.needsLocals;
2922
- needsGlobals = needsGlobals || result.needsGlobals;
2923
- }
2924
- return {
2925
- code: statements.join("\n"),
2926
- needsEvaluator,
2927
- needsLocals,
2928
- needsGlobals
2929
- };
2930
- }
2931
- function compileNode(node) {
2932
- if (node.type === "command") {
2933
- return compileCommand(node);
2934
- }
2935
- return null;
2936
- }
2937
- function compileCommand(cmd) {
2938
- const target = cmd.target ? compileTarget(cmd.target) : "m";
2939
- switch (cmd.name) {
2940
- case "toggle":
2941
- return compileToggle(cmd.args, target);
2942
- case "add":
2943
- return compileAdd(cmd.args, target);
2944
- case "remove":
2945
- return compileRemove(cmd.args, target);
2946
- case "removeClass":
2947
- return compileRemoveClass(cmd.args, target);
2948
- case "show":
2949
- return {
2950
- code: `${target}.style.display = ''`,
2951
- needsEvaluator: false,
2952
- needsLocals: false,
2953
- needsGlobals: false
2954
- };
2955
- case "hide":
2956
- return {
2957
- code: `${target}.style.display = 'none'`,
2958
- needsEvaluator: false,
2959
- needsLocals: false,
2960
- needsGlobals: false
2961
- };
2962
- case "focus":
2963
- return {
2964
- code: `${target}.focus()`,
2965
- needsEvaluator: false,
2966
- needsLocals: false,
2967
- needsGlobals: false
2968
- };
2969
- case "blur":
2970
- return {
2971
- code: `${target}.blur()`,
2972
- needsEvaluator: false,
2973
- needsLocals: false,
2974
- needsGlobals: false
2975
- };
2976
- case "log":
2977
- return compileLog(cmd.args);
2978
- case "set":
2979
- return compileSet(cmd.args);
2980
- case "increment":
2981
- return compileIncrement(cmd.args);
2982
- case "decrement":
2983
- return compileDecrement(cmd.args);
2984
- case "put":
2985
- return compilePut(cmd.args, target, cmd.modifier);
2986
- case "send":
2987
- case "trigger":
2988
- return compileTrigger(cmd.args, target);
2989
- case "wait":
2990
- return compileWait(cmd.args);
2991
- default:
2992
- return null;
2993
- }
2994
- }
2995
- function compileToggle(args, target) {
2996
- const className = extractClassName(args[0]);
2997
- if (!className) return null;
2998
- if (target === "m") {
2999
- return {
3000
- code: `m.classList.toggle('${className}')`,
3001
- needsEvaluator: false,
3002
- needsLocals: false,
3003
- needsGlobals: false
3004
- };
3005
- }
3006
- return {
3007
- code: `${target}.forEach(el => el.classList.toggle('${className}'))`,
3008
- needsEvaluator: false,
3009
- needsLocals: false,
3010
- needsGlobals: false
3011
- };
3012
- }
3013
- function compileAdd(args, target) {
3014
- const className = extractClassName(args[0]);
3015
- if (!className) return null;
3016
- if (target === "m") {
3017
- return {
3018
- code: `m.classList.add('${className}')`,
3019
- needsEvaluator: false,
3020
- needsLocals: false,
3021
- needsGlobals: false
3022
- };
3023
- }
3024
- return {
3025
- code: `${target}.forEach(el => el.classList.add('${className}'))`,
3026
- needsEvaluator: false,
3027
- needsLocals: false,
3028
- needsGlobals: false
3029
- };
3030
- }
3031
- function compileRemove(args, target) {
3032
- if (!args || args.length === 0) {
3033
- return {
3034
- code: `${target}.remove()`,
3035
- needsEvaluator: false,
3036
- needsLocals: false,
3037
- needsGlobals: false
3038
- };
3039
- }
3040
- const className = extractClassName(args[0]);
3041
- if (!className) return null;
3042
- if (target === "m") {
3043
- return {
3044
- code: `m.classList.remove('${className}')`,
3045
- needsEvaluator: false,
3046
- needsLocals: false,
3047
- needsGlobals: false
3048
- };
3049
- }
3050
- return {
3051
- code: `${target}.forEach(el => el.classList.remove('${className}'))`,
3052
- needsEvaluator: false,
3053
- needsLocals: false,
3054
- needsGlobals: false
3055
- };
3056
- }
3057
- function compileRemoveClass(args, target) {
3058
- const className = extractClassName(args[0]);
3059
- if (!className) return null;
3060
- if (target === "m") {
3061
- return {
3062
- code: `m.classList.remove('${className}')`,
3063
- needsEvaluator: false,
3064
- needsLocals: false,
3065
- needsGlobals: false
3066
- };
3067
- }
3068
- return {
3069
- code: `${target}.forEach(el => el.classList.remove('${className}'))`,
3070
- needsEvaluator: false,
3071
- needsLocals: false,
3072
- needsGlobals: false
3073
- };
3074
- }
3075
- function compileLog(args) {
3076
- const compiled = args.map(compileExpression).filter(Boolean);
3077
- if (compiled.length !== args.length) {
3078
- return {
3079
- code: `console.log(${compiled.join(", ")})`,
3080
- needsEvaluator: true,
3081
- needsLocals: false,
3082
- needsGlobals: false
3083
- };
3084
- }
3085
- return {
3086
- code: `console.log(${compiled.join(", ")})`,
3087
- needsEvaluator: false,
3088
- needsLocals: false,
3089
- needsGlobals: false
3090
- };
3091
- }
3092
- function compileSet(args) {
3093
- if (args.length < 2) return null;
3094
- const [target, value] = args;
3095
- const compiledValue = compileExpression(value);
3096
- if (!compiledValue) return null;
3097
- if (target.type === "variable") {
3098
- const varNode = target;
3099
- if (varNode.scope === "local") {
3100
- const varName = varNode.name.slice(1);
3101
- return {
3102
- code: `L.${varName} = ${compiledValue}`,
3103
- needsEvaluator: false,
3104
- needsLocals: true,
3105
- needsGlobals: false
3106
- };
3107
- }
3108
- if (varNode.scope === "global") {
3109
- const varName = varNode.name.slice(1);
3110
- return {
3111
- code: `G.${varName} = ${compiledValue}`,
3112
- needsEvaluator: false,
3113
- needsLocals: false,
3114
- needsGlobals: true
3115
- };
3116
- }
3117
- }
3118
- if (target.type === "possessive") {
3119
- const possNode = target;
3120
- const obj = compileExpression(possNode.object);
3121
- const prop = possNode.property;
3122
- if (!obj) return null;
3123
- if (prop.startsWith("*")) {
3124
- const styleProp = prop.slice(1);
3125
- return {
3126
- code: `${obj}.style.${styleProp} = ${compiledValue}`,
3127
- needsEvaluator: false,
3128
- needsLocals: false,
3129
- needsGlobals: false
3130
- };
3131
- }
3132
- return {
3133
- code: `${obj}.${prop} = ${compiledValue}`,
3134
- needsEvaluator: false,
3135
- needsLocals: false,
3136
- needsGlobals: false
3137
- };
3138
- }
3139
- if (target.type === "member") {
3140
- const memNode = target;
3141
- const obj = compileExpression(memNode.object);
3142
- const prop = typeof memNode.property === "string" ? memNode.property : null;
3143
- if (!obj || !prop) return null;
3144
- if (prop.startsWith("*")) {
3145
- const styleProp = prop.slice(1);
3146
- return {
3147
- code: `${obj}.style.${styleProp} = ${compiledValue}`,
3148
- needsEvaluator: false,
3149
- needsLocals: false,
3150
- needsGlobals: false
3151
- };
3152
- }
3153
- return {
3154
- code: `${obj}.${prop} = ${compiledValue}`,
3155
- needsEvaluator: false,
3156
- needsLocals: false,
3157
- needsGlobals: false
3158
- };
3159
- }
3160
- return null;
3161
- }
3162
- function compileIncrement(args) {
3163
- if (args.length < 1) return null;
3164
- const target = args[0];
3165
- const amount = args.length > 1 ? compileExpression(args[1]) : "1";
3166
- if (target.type === "variable") {
3167
- const varNode = target;
3168
- if (varNode.scope === "local") {
3169
- const varName = varNode.name.slice(1);
3170
- return {
3171
- code: `L.${varName} = (L.${varName} || 0) + ${amount}`,
3172
- needsEvaluator: false,
3173
- needsLocals: true,
3174
- needsGlobals: false
3175
- };
3176
- }
3177
- if (varNode.scope === "global") {
3178
- const varName = varNode.name.slice(1);
3179
- return {
3180
- code: `G.${varName} = (G.${varName} || 0) + ${amount}`,
3181
- needsEvaluator: false,
3182
- needsLocals: false,
3183
- needsGlobals: true
3184
- };
3185
- }
3186
- }
3187
- const compiled = compileExpression(target);
3188
- if (compiled) {
3189
- return {
3190
- code: `${compiled}.textContent = (parseFloat(${compiled}.textContent) || 0) + ${amount}`,
3191
- needsEvaluator: false,
3192
- needsLocals: false,
3193
- needsGlobals: false
3194
- };
3195
- }
3196
- return null;
3197
- }
3198
- function compileDecrement(args) {
3199
- if (args.length < 1) return null;
3200
- const target = args[0];
3201
- const amount = args.length > 1 ? compileExpression(args[1]) : "1";
3202
- if (target.type === "variable") {
3203
- const varNode = target;
3204
- if (varNode.scope === "local") {
3205
- const varName = varNode.name.slice(1);
3206
- return {
3207
- code: `L.${varName} = (L.${varName} || 0) - ${amount}`,
3208
- needsEvaluator: false,
3209
- needsLocals: true,
3210
- needsGlobals: false
3211
- };
3212
- }
3213
- if (varNode.scope === "global") {
3214
- const varName = varNode.name.slice(1);
3215
- return {
3216
- code: `G.${varName} = (G.${varName} || 0) - ${amount}`,
3217
- needsEvaluator: false,
3218
- needsLocals: false,
3219
- needsGlobals: true
3220
- };
3221
- }
3222
- }
3223
- const compiled = compileExpression(target);
3224
- if (compiled) {
3225
- return {
3226
- code: `${compiled}.textContent = (parseFloat(${compiled}.textContent) || 0) - ${amount}`,
3227
- needsEvaluator: false,
3228
- needsLocals: false,
3229
- needsGlobals: false
3230
- };
3231
- }
3232
- return null;
3233
- }
3234
- function compilePut(args, target, modifier) {
3235
- if (args.length < 1) return null;
3236
- const content = compileExpression(args[0]);
3237
- if (!content) return null;
3238
- const mod = modifier || "into";
3239
- switch (mod) {
3240
- case "into":
3241
- return {
3242
- code: `${target}.innerHTML = ${content}`,
3243
- needsEvaluator: false,
3244
- needsLocals: false,
3245
- needsGlobals: false
3246
- };
3247
- case "before":
3248
- return {
3249
- code: `${target}.insertAdjacentHTML('beforebegin', ${content})`,
3250
- needsEvaluator: false,
3251
- needsLocals: false,
3252
- needsGlobals: false
3253
- };
3254
- case "after":
3255
- return {
3256
- code: `${target}.insertAdjacentHTML('afterend', ${content})`,
3257
- needsEvaluator: false,
3258
- needsLocals: false,
3259
- needsGlobals: false
3260
- };
3261
- case "at start of":
3262
- return {
3263
- code: `${target}.insertAdjacentHTML('afterbegin', ${content})`,
3264
- needsEvaluator: false,
3265
- needsLocals: false,
3266
- needsGlobals: false
3267
- };
3268
- case "at end of":
3269
- return {
3270
- code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
3271
- needsEvaluator: false,
3272
- needsLocals: false,
3273
- needsGlobals: false
3274
- };
3275
- default:
3276
- return {
3277
- code: `${target}.innerHTML = ${content}`,
3278
- needsEvaluator: false,
3279
- needsLocals: false,
3280
- needsGlobals: false
3281
- };
3282
- }
3283
- }
3284
- function compileTrigger(args, target) {
3285
- if (args.length < 1) return null;
3286
- const eventName = compileExpression(args[0]);
3287
- if (!eventName) return null;
3288
- return {
3289
- code: `${target}.dispatchEvent(new CustomEvent(${eventName}, { bubbles: true }))`,
3290
- needsEvaluator: false,
3291
- needsLocals: false,
3292
- needsGlobals: false
3293
- };
3294
- }
3295
- function compileWait(args) {
3296
- if (args.length < 1) return null;
3297
- const duration = compileExpression(args[0]);
3298
- if (!duration) return null;
3299
- return {
3300
- code: `await new Promise(r => setTimeout(r, ${duration}))`,
3301
- needsEvaluator: false,
3302
- needsLocals: false,
3303
- needsGlobals: false
3304
- };
3305
- }
3306
- function compileExpression(node) {
3307
- if (!node) return null;
3308
- switch (node.type) {
3309
- case "literal": {
3310
- const litNode = node;
3311
- return JSON.stringify(litNode.value);
3312
- }
3313
- case "identifier": {
3314
- const idNode = node;
3315
- const val = idNode.value;
3316
- if (val === "me" || val === "my") return "m";
3317
- if (val === "you") return "y";
3318
- if (val === "it") return "it";
3319
- if (val === "event") return "e";
3320
- if (val === "body") return "document.body";
3321
- if (val === "document") return "document";
3322
- if (val === "window") return "window";
3323
- return val;
3324
- }
3325
- case "variable": {
3326
- const varNode = node;
3327
- const name = varNode.name.slice(1);
3328
- return varNode.scope === "local" ? `L.${name}` : `G.${name}`;
3329
- }
3330
- case "selector": {
3331
- const selNode = node;
3332
- const sanitized = sanitizeSelector(selNode.value);
3333
- return `document.querySelector('${sanitized}')`;
3334
- }
3335
- case "possessive": {
3336
- const possNode = node;
3337
- const obj = compileExpression(possNode.object);
3338
- const prop = possNode.property;
3339
- if (!obj) return null;
3340
- if (prop.startsWith("*")) {
3341
- return `${obj}.style.${prop.slice(1)}`;
3342
- }
3343
- if (prop === "values") {
3344
- return `(function(el){if(el instanceof HTMLFormElement)return new FormData(el);var fd=new FormData();el.querySelectorAll('input,select,textarea').forEach(function(i){var n=i.getAttribute('name');if(n&&'value'in i)fd.append(n,i.value)});return fd})(${obj})`;
3345
- }
3346
- return `${obj}.${prop}`;
3347
- }
3348
- case "member": {
3349
- const memNode = node;
3350
- const obj = compileExpression(memNode.object);
3351
- const prop = typeof memNode.property === "string" ? memNode.property : null;
3352
- if (!obj || !prop) return null;
3353
- if (prop.startsWith("*")) {
3354
- return `${obj}.style.${prop.slice(1)}`;
3355
- }
3356
- return `${obj}.${prop}`;
3357
- }
3358
- case "binary": {
3359
- const binNode = node;
3360
- const left = compileExpression(binNode.left);
3361
- const right = compileExpression(binNode.right);
3362
- if (!left || !right) return null;
3363
- const op = binNode.operator;
3364
- switch (op) {
3365
- case "+":
3366
- case "-":
3367
- case "*":
3368
- case "/":
3369
- case "%":
3370
- return `(${left} ${op} ${right})`;
3371
- case "==":
3372
- case "is":
3373
- return `(${left} == ${right})`;
3374
- case "!=":
3375
- case "is not":
3376
- return `(${left} != ${right})`;
3377
- case "<":
3378
- case ">":
3379
- case "<=":
3380
- case ">=":
3381
- return `(${left} ${op} ${right})`;
3382
- case "and":
3383
- case "&&":
3384
- return `(${left} && ${right})`;
3385
- case "or":
3386
- case "||":
3387
- return `(${left} || ${right})`;
3388
- case "has": {
3389
- const rightSel = binNode.right;
3390
- const className = rightSel.value?.slice(1) || "";
3391
- const sanitizedClass = sanitizeClassName(className);
3392
- if (!sanitizedClass) return null;
3393
- return `${left}.classList.contains('${sanitizedClass}')`;
3394
- }
3395
- default:
3396
- return null;
3397
- }
3398
- }
3399
- case "call": {
3400
- const callNode = node;
3401
- const callee = callNode.callee;
3402
- const args = (callNode.args || []).map(compileExpression);
3403
- if (args.some((a) => a === null)) return null;
3404
- if (callee.type === "member" || callee.type === "possessive") {
3405
- const memCallee = callee;
3406
- const obj = compileExpression(memCallee.object);
3407
- const prop = typeof memCallee.property === "string" ? memCallee.property : null;
3408
- if (!obj || !prop) return null;
3409
- return `${obj}.${prop}(${args.join(", ")})`;
3410
- }
3411
- const fn = compileExpression(callee);
3412
- if (!fn) return null;
3413
- return `${fn}(${args.join(", ")})`;
3414
- }
3415
- case "positional": {
3416
- const posNode = node;
3417
- const position = posNode.position;
3418
- const target = posNode.target;
3419
- switch (position) {
3420
- case "first":
3421
- if (target?.type === "selector") {
3422
- const selNode = target;
3423
- const sanitized = sanitizeSelector(selNode.value);
3424
- return `document.querySelector('${sanitized}')`;
3425
- }
3426
- return null;
3427
- // Arrays need runtime
3428
- case "last":
3429
- if (target?.type === "selector") {
3430
- const selNode = target;
3431
- const sanitized = sanitizeSelector(selNode.value);
3432
- return `(()=>{const e=document.querySelectorAll('${sanitized}');return e[e.length-1]})()`;
3433
- }
3434
- return null;
3435
- // Arrays need runtime
3436
- case "next":
3437
- return "m.nextElementSibling";
3438
- case "previous":
3439
- return "m.previousElementSibling";
3440
- case "closest":
3441
- if (target?.type === "selector") {
3442
- const selNode = target;
3443
- const sanitized = sanitizeSelector(selNode.value);
3444
- return `m.closest('${sanitized}')`;
3445
- }
3446
- return null;
3447
- case "parent":
3448
- return "m.parentElement";
3449
- default:
3450
- return null;
3451
- }
3452
- }
3453
- default:
3454
- return null;
3455
- }
3456
- }
3457
- function compileTarget(target) {
3458
- if (target.type === "identifier") {
3459
- const idNode = target;
3460
- if (idNode.value === "me" || idNode.value === "my") return "m";
3461
- if (idNode.value === "you") return "y";
3462
- }
3463
- if (target.type === "selector") {
3464
- const selNode = target;
3465
- const sanitized = sanitizeSelector(selNode.value);
3466
- return `document.querySelectorAll('${sanitized}')`;
3467
- }
3468
- const compiled = compileExpression(target);
3469
- return compiled || "m";
3470
- }
3471
- function extractClassName(node) {
3472
- if (!node) return null;
3473
- if (node.type === "selector") {
3474
- const selNode = node;
3475
- const val = selNode.value;
3476
- const rawClass = val.startsWith(".") ? val.slice(1) : val;
3477
- return sanitizeClassName(rawClass);
3478
- }
3479
- if (node.type === "literal" || node.type === "string") {
3480
- const litNode = node;
3481
- const val = litNode.value;
3482
- if (typeof val === "string") {
3483
- const rawClass = val.startsWith(".") ? val.slice(1) : val;
3484
- return sanitizeClassName(rawClass);
3485
- }
3486
- }
3487
- if (node.type === "identifier") {
3488
- const idNode = node;
3489
- return sanitizeClassName(idNode.value);
3490
- }
3491
- return null;
3492
- }
3493
-
3494
- // src/compiled-generator.ts
3495
- function generateSourceMapTable(handlers) {
3496
- if (handlers.length === 0) return "";
3497
- const lines = [
3498
- " *",
3499
- " * Handler Source Map:",
3500
- " * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"
3501
- ];
3502
- for (const h of handlers) {
3503
- const original = h.original.replace(/\n/g, " ").slice(0, 40);
3504
- const truncated = original.length < h.original.length ? original + "..." : original;
3505
- lines.push(` * \u2502 ${h.id.padEnd(20)} \u2190 ${truncated.padEnd(40)} \u2502`);
3506
- }
3507
- lines.push(" * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
3508
- lines.push(" *");
3509
- return lines.join("\n");
3510
- }
3511
- function generateCompiledBundle(options) {
3512
- const {
3513
- handlers,
3514
- needsLocals,
3515
- needsGlobals,
3516
- globalName = "hyperfixi",
3517
- htmx = false,
3518
- debug = false
3519
- } = options;
3520
- const handlerEntries = handlers.map((h) => {
3521
- const isAsync = h.code.includes("await");
3522
- const fn = isAsync ? `async(m,e,y)=>{${h.code}}` : `(m,e,y)=>{${h.code}}`;
3523
- if (debug) {
3524
- const escapedOriginal = h.original.replace(/\*/g, "\\*").replace(/\n/g, " ");
3525
- return `
3526
- /* ${escapedOriginal} */
3527
- ${h.id}:${fn}`;
3528
- }
3529
- return `${h.id}:${fn}`;
3530
- });
3531
- const registrations = handlers.map((h) => {
3532
- const modsCode = generateModifiersCode(h);
3533
- return ` case '${h.id}': r(el,'${h.event}',H.${h.id}${modsCode}); break;`;
3534
- });
3535
- const localsCode = needsLocals ? "const L={};" : "";
3536
- const globalsCode = needsGlobals ? "const G={};" : "";
3537
- const sourceMapTable = debug ? generateSourceMapTable(handlers) : "";
3538
- return `/**
3539
- * HyperFixi Compiled Bundle (Auto-Generated)
3540
- *
3541
- * Pre-compiled hyperscript - no runtime parser needed!
3542
- * Handlers: ${handlers.length}
3543
- *
3544
- * DO NOT EDIT - Regenerate with build.
3545
- ${sourceMapTable} */
3546
-
3547
- ${localsCode}${globalsCode}
3548
- const H={${handlerEntries.join(",")}};
3549
-
3550
- function r(el,ev,fn${hasModifiers(handlers) ? ",m" : ""}){
3551
- let h=e=>fn(el,e,e.target);
3552
- ${generateModifierWrappers(handlers)}
3553
- el.addEventListener(ev,h${hasOnce(handlers) ? ",{once:m?.o}" : ""});
3554
- }
3555
-
3556
- function init(root=document){
3557
- root.querySelectorAll('[data-h]').forEach(el=>{
3558
- switch(el.dataset.h){
3559
- ${registrations.join("\n")}
3560
- }
3561
- });
3562
- }
3563
-
3564
- ${htmx ? `document.addEventListener('htmx:afterSettle',e=>init(e.detail?.target));` : ""}
3565
-
3566
- const api={
3567
- version:'1.0.0-compiled',
3568
- commands:${JSON.stringify(handlers.map((h) => h.id))},
3569
- init,
3570
- process:init,
3571
- };
2133
+ register(...everything);
2134
+ Object.assign(api, {
2135
+ bundle: { generator: '@hyperfixi/vite-plugin', modules: ['everything'], commands: [], blocks: [], languages: [] },
2136
+ });
3572
2137
 
3573
- if(typeof window!=='undefined'){
3574
- window.${globalName}=api;
3575
- document.readyState==='loading'
3576
- ?document.addEventListener('DOMContentLoaded',()=>init())
3577
- :init();
2138
+ if (typeof window !== 'undefined') {
2139
+ window.hyperfixi = api;
2140
+ boot();
3578
2141
  }
3579
2142
 
3580
2143
  export default api;
3581
- export{api,init};
2144
+ export { api };
3582
2145
  `;
3583
- }
3584
- function generateModifiersCode(h) {
3585
- const mods = [];
3586
- if (h.modifiers.prevent) mods.push("p:1");
3587
- if (h.modifiers.stop) mods.push("s:1");
3588
- if (h.modifiers.once) mods.push("o:1");
3589
- if (h.modifiers.debounce) mods.push(`d:${h.modifiers.debounce}`);
3590
- if (h.modifiers.throttle) mods.push(`t:${h.modifiers.throttle}`);
3591
- if (mods.length === 0) return "";
3592
- return `,{${mods.join(",")}}`;
3593
- }
3594
- function hasModifiers(handlers) {
3595
- return handlers.some(
3596
- (h) => h.modifiers.prevent || h.modifiers.stop || h.modifiers.once || h.modifiers.debounce || h.modifiers.throttle
3597
- );
3598
- }
3599
- function hasOnce(handlers) {
3600
- return handlers.some((h) => h.modifiers.once);
3601
- }
3602
- function generateModifierWrappers(handlers) {
3603
- const lines = [];
3604
- if (handlers.some((h) => h.modifiers.prevent || h.modifiers.stop)) {
3605
- lines.push(
3606
- ` if(m?.p||m?.s){const _h=h;h=e=>{m.p&&e.preventDefault();m.s&&e.stopPropagation();_h(e.currentTarget,e,e.target)};}`
3607
- );
3608
- }
3609
- if (handlers.some((h) => h.modifiers.debounce)) {
3610
- lines.push(
3611
- ` if(m?.d){let t;const _h=h;h=e=>{clearTimeout(t);t=setTimeout(()=>_h(e.currentTarget,e,e.target),m.d)};}`
3612
- );
3613
- }
3614
- if (handlers.some((h) => h.modifiers.throttle)) {
3615
- lines.push(
3616
- ` if(m?.t){let l=0;const _h=h;h=e=>{const n=Date.now();if(n-l>=m.t){l=n;_h(e.currentTarget,e,e.target)}};}`
3617
- );
3618
2146
  }
3619
- return lines.join("\n");
3620
- }
3621
-
3622
- // src/html-transformer.ts
3623
- function transformHTML(html, handlerMap, fallbackScripts) {
3624
- let modified = false;
3625
- const fallbacks = [];
3626
- const patterns = [
3627
- /(\s)_\s*=\s*"([^"]+)"/g,
3628
- // _="..."
3629
- /(\s)_\s*=\s*'([^']+)'/g,
3630
- // _='...'
3631
- /(\s)_\s*=\s*`([^`]+)`/g
3632
- // _=`...`
3633
- ];
3634
- let result = html;
3635
- for (const pattern of patterns) {
3636
- result = result.replace(pattern, (match, whitespace, script) => {
3637
- const handlerId = handlerMap.get(script);
3638
- if (handlerId) {
3639
- modified = true;
3640
- return `${whitespace}data-h="${handlerId}"`;
3641
- }
3642
- if (fallbackScripts.has(script)) {
3643
- fallbacks.push(script);
3644
- return match;
3645
- }
3646
- fallbacks.push(script);
3647
- return match;
3648
- });
3649
- }
3650
- return {
3651
- code: result,
3652
- modified,
3653
- fallbacks
3654
- };
3655
- }
3656
- function extractScripts(html) {
3657
- const scripts = [];
3658
- const patterns = [/_\s*=\s*"([^"]+)"/g, /_\s*=\s*'([^']+)'/g, /_\s*=\s*`([^`]+)`/g];
3659
- for (const pattern of patterns) {
3660
- let match;
3661
- while (match = pattern.exec(html)) {
3662
- scripts.push(match[1]);
3663
- }
3664
- }
3665
- return scripts;
3666
- }
2147
+ };
3667
2148
 
3668
2149
  // src/server-bridge-integration.ts
3669
2150
  var import_node_path = require("path");
@@ -3967,10 +2448,15 @@ function computeUsageHash(usage) {
3967
2448
  usage.htmx.needsReactivity
3968
2449
  ].map((f) => f ? "1" : "0").join("") : "none";
3969
2450
  const reactivity = `${usage.needsReactivity ? 1 : 0}${usage.needsBindToProperty ? 1 : 0}`;
3970
- return `${commands}|${blocks}|${usage.positional}|${languages}|${htmx}|${reactivity}`;
2451
+ const extras = `${usage.needsConstruct ? 1 : 0}${usage.needsCookies ? 1 : 0}`;
2452
+ return `${commands}|${blocks}|${usage.positional}|${languages}|${htmx}|${reactivity}|${extras}`;
3971
2453
  }
3972
2454
  function hyperfixi(options = {}) {
3973
- const mode = options.mode ?? "interpret";
2455
+ if (options.mode === "compile") {
2456
+ console.warn(
2457
+ `[hyperfixi] mode: "compile" was removed in 4.0 (it compiled with @hyperfixi/core's parser); building the bundle on @hyperfixi/engine instead.`
2458
+ );
2459
+ }
3974
2460
  const scanner = new Scanner(options);
3975
2461
  const aggregator = new Aggregator();
3976
2462
  const generator = new Generator(options);
@@ -3980,11 +2466,6 @@ function hyperfixi(options = {}) {
3980
2466
  let cachedBundle = null;
3981
2467
  let lastUsageHash = "";
3982
2468
  let isDev = false;
3983
- const compiledHandlers = [];
3984
- const handlerMap = /* @__PURE__ */ new Map();
3985
- const fallbackScripts = /* @__PURE__ */ new Set();
3986
- let needsLocals = false;
3987
- let needsGlobals = false;
3988
2469
  function invalidateVirtualModule() {
3989
2470
  if (!server) return;
3990
2471
  const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_MODULE_ID);
@@ -3996,32 +2477,6 @@ function hyperfixi(options = {}) {
3996
2477
  }
3997
2478
  }
3998
2479
  }
3999
- function compileScript(script) {
4000
- if (handlerMap.has(script)) {
4001
- return handlerMap.get(script);
4002
- }
4003
- if (fallbackScripts.has(script)) {
4004
- return null;
4005
- }
4006
- const handler = compile(script);
4007
- if (handler) {
4008
- compiledHandlers.push(handler);
4009
- handlerMap.set(script, handler.id);
4010
- if (handler.needsEvaluator) {
4011
- if (handler.code.includes("L.")) needsLocals = true;
4012
- if (handler.code.includes("G.")) needsGlobals = true;
4013
- }
4014
- if (options.debug) {
4015
- console.log(`[hyperfixi] Compiled: "${script}" -> ${handler.id}`);
4016
- }
4017
- return handler.id;
4018
- }
4019
- fallbackScripts.add(script);
4020
- if (options.debug) {
4021
- console.log(`[hyperfixi] Fallback (not compilable): "${script}"`);
4022
- }
4023
- return null;
4024
- }
4025
2480
  function generateInterpretBundle() {
4026
2481
  const usage = aggregator.getUsage();
4027
2482
  const usageHash = computeUsageHash(usage);
@@ -4040,30 +2495,7 @@ function hyperfixi(options = {}) {
4040
2495
  }
4041
2496
  return cachedBundle;
4042
2497
  }
4043
- function generateCompiledBundleCode() {
4044
- if (fallbackScripts.size > 0) {
4045
- if (options.debug) {
4046
- console.log(`[hyperfixi] ${fallbackScripts.size} scripts need interpreter fallback`);
4047
- }
4048
- return generateInterpretBundle();
4049
- }
4050
- const bundle = generateCompiledBundle({
4051
- handlers: compiledHandlers,
4052
- needsLocals,
4053
- needsGlobals,
4054
- globalName: options.globalName,
4055
- htmx: options.htmx,
4056
- debug: options.debug
4057
- });
4058
- if (options.debug) {
4059
- console.log(`[hyperfixi] Compiled bundle: ${compiledHandlers.length} handlers`);
4060
- }
4061
- return bundle;
4062
- }
4063
2498
  function generateBundle() {
4064
- if (mode === "compile") {
4065
- return generateCompiledBundleCode();
4066
- }
4067
2499
  return generateInterpretBundle();
4068
2500
  }
4069
2501
  return {
@@ -4141,20 +2573,6 @@ function hyperfixi(options = {}) {
4141
2573
  if (!scanner.shouldScan(id)) {
4142
2574
  return null;
4143
2575
  }
4144
- if (mode === "compile" && !isDev) {
4145
- const scripts = extractScripts(code);
4146
- for (const script of scripts) {
4147
- compileScript(script);
4148
- }
4149
- const result = transformHTML(code, handlerMap, fallbackScripts);
4150
- if (result.modified) {
4151
- if (options.debug) {
4152
- console.log(`[hyperfixi] Transformed: ${id.split("/").pop()}`);
4153
- }
4154
- return { code: result.code, map: null };
4155
- }
4156
- return null;
4157
- }
4158
2576
  const usage = scanner.scan(code, id);
4159
2577
  const changed = aggregator.add(id, usage);
4160
2578
  if (domainScanner && domainAggregator && domainScanner.hasAnyDomainUsage(code)) {
@@ -4208,17 +2626,6 @@ function hyperfixi(options = {}) {
4208
2626
  */
4209
2627
  async buildStart() {
4210
2628
  if (!isDev) {
4211
- if (mode === "compile") {
4212
- resetCompiler();
4213
- compiledHandlers.length = 0;
4214
- handlerMap.clear();
4215
- fallbackScripts.clear();
4216
- needsLocals = false;
4217
- needsGlobals = false;
4218
- if (options.debug) {
4219
- console.log("[hyperfixi] Compile mode: ready for build");
4220
- }
4221
- }
4222
2629
  const cwd = process.cwd();
4223
2630
  if (options.debug) {
4224
2631
  console.log("[hyperfixi] Scanning project for hyperscript usage...");
@@ -4244,22 +2651,6 @@ function hyperfixi(options = {}) {
4244
2651
  console.log("[hyperfixi] Domain build scan:", domainSummary);
4245
2652
  }
4246
2653
  }
4247
- if (mode === "compile") {
4248
- const usage = aggregator.getUsage();
4249
- if (usage.detectedLanguages.size > 0) {
4250
- const aliases = getMultilingualCommandAliases(
4251
- usage.detectedLanguages
4252
- );
4253
- if (Object.keys(aliases).length > 0) {
4254
- setMultilingualAliases(aliases);
4255
- if (options.debug) {
4256
- console.log(
4257
- `[hyperfixi] Compile mode: configured ${Object.keys(aliases).length} multilingual aliases`
4258
- );
4259
- }
4260
- }
4261
- }
4262
- }
4263
2654
  if (options.debug) {
4264
2655
  const summary = aggregator.getSummary();
4265
2656
  console.log(`[hyperfixi] Found ${summary.fileCount} files with hyperscript:`, {
@@ -4284,17 +2675,14 @@ var index_default = hyperfixi;
4284
2675
  REGIONS,
4285
2676
  SUPPORTED_LANGUAGES,
4286
2677
  clearCustomKeywords,
4287
- clearSemanticParser,
4288
2678
  computeUsageHash,
4289
2679
  descriptorToScanRule,
4290
2680
  detectLanguages,
4291
2681
  getAllLanguageCodes,
4292
2682
  getKeywordsForLanguage,
4293
2683
  getOptimalRegion,
4294
- hasSemanticParser,
4295
2684
  hyperfixi,
4296
2685
  isNonLatinLanguage,
4297
- registerCustomKeywords,
4298
- setSemanticParser
2686
+ registerCustomKeywords
4299
2687
  });
4300
2688
  //# sourceMappingURL=index.cjs.map