@hyperfixi/vite-plugin 2.3.1 → 2.4.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.js CHANGED
@@ -18,6 +18,8 @@ var SUPPORTED_LANGUAGES = [
18
18
  // East Asian
19
19
  "ar",
20
20
  // RTL (Arabic script)
21
+ "he",
22
+ // RTL (Hebrew script)
21
23
  "hi",
22
24
  "bn",
23
25
  // South Asian (Indic scripts)
@@ -26,6 +28,8 @@ var SUPPORTED_LANGUAGES = [
26
28
  "tr",
27
29
  // Agglutinative Latin
28
30
  "id",
31
+ "ms",
32
+ // Malay (Latin script, closely related to id)
29
33
  "sw",
30
34
  "qu",
31
35
  // Other
@@ -73,7 +77,6 @@ var JAPANESE_KEYWORDS = /* @__PURE__ */ new Set([
73
77
  "\u975E\u8868\u793A\u306B\u3059\u308B",
74
78
  "\u8A2D\u5B9A",
75
79
  "\u8A2D\u5B9A\u3059\u308B",
76
- "\u30BB\u30C3\u30C8",
77
80
  "\u5897\u52A0",
78
81
  "\u5897\u3084\u3059",
79
82
  "\u30A4\u30F3\u30AF\u30EA\u30E1\u30F3\u30C8",
@@ -86,7 +89,6 @@ var JAPANESE_KEYWORDS = /* @__PURE__ */ new Set([
86
89
  "\u9001\u308B",
87
90
  "\u9001\u4FE1",
88
91
  "\u3082\u3057",
89
- "\u6761\u4EF6",
90
92
  "\u305D\u3046\u3067\u306A\u3051\u308C\u3070",
91
93
  "\u305D\u308C\u4EE5\u5916",
92
94
  "\u7E70\u308A\u8FD4\u3057",
@@ -99,7 +101,6 @@ var JAPANESE_KEYWORDS = /* @__PURE__ */ new Set([
99
101
  ]);
100
102
  var KOREAN_KEYWORDS = /* @__PURE__ */ new Set([
101
103
  "\uD1A0\uAE00",
102
- "\uC804\uD658",
103
104
  "\uCD94\uAC00",
104
105
  "\uC81C\uAC70",
105
106
  "\uC0AD\uC81C",
@@ -139,16 +140,15 @@ var CHINESE_KEYWORDS = /* @__PURE__ */ new Set([
139
140
  "\u5426\u5219",
140
141
  "\u91CD\u590D",
141
142
  "\u7B49\u5F85",
142
- "\u5F53"
143
+ "\u6301\u7EED"
143
144
  ]);
144
145
  var ARABIC_KEYWORDS = /* @__PURE__ */ new Set([
145
- "\u0628\u062F\u0644",
146
146
  "\u0628\u062F\u0651\u0644",
147
+ "\u0628\u062F\u0644",
147
148
  "\u063A\u064A\u0651\u0631",
148
149
  "\u063A\u064A\u0631",
149
150
  "\u0623\u0636\u0641",
150
151
  "\u0627\u0636\u0641",
151
- "\u0632\u0650\u062F",
152
152
  "\u0627\u062D\u0630\u0641",
153
153
  "\u0623\u0632\u0644",
154
154
  "\u0627\u0645\u0633\u062D",
@@ -158,11 +158,15 @@ var ARABIC_KEYWORDS = /* @__PURE__ */ new Set([
158
158
  "\u0627\u062E\u0641",
159
159
  "\u0623\u062E\u0641\u0650",
160
160
  "\u0627\u062E\u0641\u064A",
161
+ "\u0623\u062E\u0641",
161
162
  "\u0627\u0636\u0628\u0637",
162
163
  "\u0639\u064A\u0651\u0646",
163
164
  "\u062D\u062F\u062F",
165
+ "\u0632\u0650\u062F",
166
+ "\u0632\u062F",
164
167
  "\u0627\u0631\u0641\u0639",
165
168
  "\u0623\u0646\u0642\u0635",
169
+ "\u0627\u0646\u0642\u0635",
166
170
  "\u0642\u0644\u0644",
167
171
  "\u062A\u0634\u063A\u064A\u0644",
168
172
  "\u0623\u0637\u0644\u0642",
@@ -175,9 +179,37 @@ var ARABIC_KEYWORDS = /* @__PURE__ */ new Set([
175
179
  "\u0627\u0646\u062A\u0638\u0631",
176
180
  "\u0628\u064A\u0646\u0645\u0627"
177
181
  ]);
182
+ var HE_KEYWORDS = /* @__PURE__ */ new Set([
183
+ "\u05DE\u05EA\u05D2",
184
+ "\u05D4\u05D3\u05DC\u05E7 \u05DB\u05D1\u05D4",
185
+ "\u05D4\u05D5\u05E1\u05E3",
186
+ "\u05D4\u05E1\u05E8",
187
+ "\u05DE\u05D7\u05E7",
188
+ "\u05D4\u05D5\u05E8\u05D3",
189
+ "\u05D4\u05E8\u05D0\u05D4",
190
+ "\u05D4\u05E6\u05D2",
191
+ "\u05D4\u05E1\u05EA\u05E8",
192
+ "\u05D4\u05D7\u05D1\u05D0",
193
+ "\u05E7\u05D1\u05E2",
194
+ "\u05D4\u05D2\u05D3\u05E8",
195
+ "\u05D4\u05D2\u05D3\u05DC",
196
+ "\u05D4\u05D5\u05E1\u05E3 \u05D0\u05D7\u05D3",
197
+ "\u05D4\u05E7\u05D8\u05DF",
198
+ "\u05D4\u05E4\u05D7\u05EA",
199
+ "\u05D4\u05E4\u05E2\u05DC",
200
+ "\u05E9\u05D2\u05E8",
201
+ "\u05E9\u05DC\u05D7",
202
+ "\u05D0\u05DD",
203
+ "\u05D0\u05D7\u05E8\u05EA",
204
+ "\u05D0\u05DD \u05DC\u05D0",
205
+ "\u05D7\u05D6\u05D5\u05E8",
206
+ "\u05D7\u05DB\u05D4",
207
+ "\u05D4\u05DE\u05EA\u05DF",
208
+ "\u05DB\u05DC \u05E2\u05D5\u05D3",
209
+ "\u05D1\u05D6\u05DE\u05DF"
210
+ ]);
178
211
  var SPANISH_KEYWORDS = /* @__PURE__ */ new Set([
179
212
  "alternar",
180
- "cambiar",
181
213
  "conmutar",
182
214
  "agregar",
183
215
  "a\xF1adir",
@@ -185,6 +217,7 @@ var SPANISH_KEYWORDS = /* @__PURE__ */ new Set([
185
217
  "eliminar",
186
218
  "remover",
187
219
  "sacar",
220
+ "borrar",
188
221
  "mostrar",
189
222
  "ense\xF1ar",
190
223
  "ocultar",
@@ -204,11 +237,16 @@ var SPANISH_KEYWORDS = /* @__PURE__ */ new Set([
204
237
  "de lo contrario",
205
238
  "repetir",
206
239
  "esperar",
207
- "mientras"
240
+ "mientras",
241
+ "primero",
242
+ "primera",
243
+ "\xFAltimo",
244
+ "ultima",
245
+ "siguiente",
246
+ "anterior"
208
247
  ]);
209
248
  var PORTUGUESE_KEYWORDS = /* @__PURE__ */ new Set([
210
249
  "alternar",
211
- "trocar",
212
250
  "adicionar",
213
251
  "acrescentar",
214
252
  "remover",
@@ -236,7 +274,6 @@ var PORTUGUESE_KEYWORDS = /* @__PURE__ */ new Set([
236
274
  ]);
237
275
  var FRENCH_KEYWORDS = /* @__PURE__ */ new Set([
238
276
  "basculer",
239
- "permuter",
240
277
  "alterner",
241
278
  "ajouter",
242
279
  "supprimer",
@@ -278,8 +315,8 @@ var GERMAN_KEYWORDS = /* @__PURE__ */ new Set([
278
315
  "ausl\xF6sen",
279
316
  "senden",
280
317
  "schicken",
281
- "wenn",
282
318
  "falls",
319
+ "sofern",
283
320
  "sonst",
284
321
  "ansonsten",
285
322
  "wiederholen",
@@ -296,7 +333,6 @@ var TURKISH_KEYWORDS = /* @__PURE__ */ new Set([
296
333
  "g\xF6ster",
297
334
  "gizle",
298
335
  "ayarla",
299
- "yap",
300
336
  "belirle",
301
337
  "art\u0131r",
302
338
  "azalt",
@@ -306,12 +342,10 @@ var TURKISH_KEYWORDS = /* @__PURE__ */ new Set([
306
342
  "yoksa",
307
343
  "tekrarla",
308
344
  "bekle",
309
- "iken"
345
+ "s\xFCresince"
310
346
  ]);
311
347
  var INDONESIAN_KEYWORDS = /* @__PURE__ */ new Set([
312
348
  "alihkan",
313
- "ganti",
314
- "tukar",
315
349
  "tambah",
316
350
  "tambahkan",
317
351
  "hapus",
@@ -339,11 +373,33 @@ var INDONESIAN_KEYWORDS = /* @__PURE__ */ new Set([
339
373
  "tunggu",
340
374
  "selama"
341
375
  ]);
376
+ var MS_KEYWORDS = /* @__PURE__ */ new Set([
377
+ "togol",
378
+ "tukar",
379
+ "tambah",
380
+ "buang",
381
+ "padam",
382
+ "tunjuk",
383
+ "sembunyi",
384
+ "sorok",
385
+ "tetapkan",
386
+ "setkan",
387
+ "tambah_satu",
388
+ "kurang_satu",
389
+ "cetuskan",
390
+ "hantar",
391
+ "jika",
392
+ "kalau",
393
+ "kalau_tidak",
394
+ "jika_tidak",
395
+ "ulang",
396
+ "tunggu",
397
+ "selagi",
398
+ "semasa"
399
+ ]);
342
400
  var SWAHILI_KEYWORDS = /* @__PURE__ */ new Set([
343
401
  "badilisha",
344
- "geuza",
345
402
  "ongeza",
346
- "weka",
347
403
  "ondoa",
348
404
  "futa",
349
405
  "toa",
@@ -351,9 +407,9 @@ var SWAHILI_KEYWORDS = /* @__PURE__ */ new Set([
351
407
  "ficha",
352
408
  "mficho",
353
409
  "seti",
410
+ "ongezeko",
354
411
  "punguza",
355
412
  "chochea",
356
- "anzisha",
357
413
  "tuma",
358
414
  "peleka",
359
415
  "kama",
@@ -363,12 +419,11 @@ var SWAHILI_KEYWORDS = /* @__PURE__ */ new Set([
363
419
  "rudia",
364
420
  "subiri",
365
421
  "ngoja",
366
- "wakati"
422
+ "kadri"
367
423
  ]);
368
424
  var QUECHUA_KEYWORDS = /* @__PURE__ */ new Set([
369
425
  "t",
370
426
  "tikray",
371
- "kutichiy",
372
427
  "yapay",
373
428
  "yapaykuy",
374
429
  "qichuy",
@@ -378,11 +433,12 @@ var QUECHUA_KEYWORDS = /* @__PURE__ */ new Set([
378
433
  "qawachiy",
379
434
  "pakay",
380
435
  "pakakuy",
381
- "churay",
436
+ "churanay",
382
437
  "kamaykuy",
383
438
  "yapachiy",
384
439
  "pisiyachiy",
385
- "qallarichiy",
440
+ "kuyuchiy",
441
+ "kichay",
386
442
  "kachay",
387
443
  "apachiy",
388
444
  "sichus",
@@ -396,7 +452,6 @@ var QUECHUA_KEYWORDS = /* @__PURE__ */ new Set([
396
452
  var ITALIAN_KEYWORDS = /* @__PURE__ */ new Set([
397
453
  "commutare",
398
454
  "alternare",
399
- "cambiare",
400
455
  "aggiungere",
401
456
  "aggiungi",
402
457
  "rimuovere",
@@ -438,7 +493,6 @@ var VIETNAMESE_KEYWORDS = /* @__PURE__ */ new Set([
438
493
  "gi\u1EA5u",
439
494
  "g\xE1n",
440
495
  "thi\u1EBFt l\u1EADp",
441
- "\u0111\u1EB7t",
442
496
  "t\u0103ng",
443
497
  "t\u0103ng l\xEAn",
444
498
  "gi\u1EA3m",
@@ -473,10 +527,6 @@ var POLISH_KEYWORDS = /* @__PURE__ */ new Set([
473
527
  "zwi\u0119ksz",
474
528
  "zwieksz",
475
529
  "zmniejsz",
476
- "uzyskaj",
477
- "pobierz",
478
- "wywo\u0142aj",
479
- "wywolaj",
480
530
  "wyzw\xF3l",
481
531
  "wyzwol",
482
532
  "wy\u015Blij",
@@ -518,8 +568,9 @@ var RUSSIAN_KEYWORDS = /* @__PURE__ */ new Set([
518
568
  "\u0443\u0432\u0435\u043B\u0438\u0447\u044C",
519
569
  "\u0443\u043C\u0435\u043D\u044C\u0448\u0438\u0442\u044C",
520
570
  "\u0443\u043C\u0435\u043D\u044C\u0448\u0438",
521
- "\u0432\u044B\u0437\u0432\u0430\u0442\u044C",
522
- "\u0432\u044B\u0437\u043E\u0432\u0438",
571
+ "\u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
572
+ "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C",
573
+ "\u0437\u0430\u043F\u0443\u0441\u0442\u0438",
523
574
  "\u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C",
524
575
  "\u043E\u0442\u043F\u0440\u0430\u0432\u044C",
525
576
  "\u0435\u0441\u043B\u0438",
@@ -554,8 +605,9 @@ var UKRAINIAN_KEYWORDS = /* @__PURE__ */ new Set([
554
605
  "\u0437\u0431\u0456\u043B\u044C\u0448",
555
606
  "\u0437\u043C\u0435\u043D\u0448\u0438\u0442\u0438",
556
607
  "\u0437\u043C\u0435\u043D\u0448",
557
- "\u0432\u0438\u043A\u043B\u0438\u043A\u0430\u0442\u0438",
558
- "\u0432\u0438\u043A\u043B\u0438\u0447",
608
+ "\u0456\u043D\u0456\u0446\u0456\u044E\u0432\u0430\u0442\u0438",
609
+ "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438",
610
+ "\u0437\u0430\u043F\u0443\u0441\u0442\u0438",
559
611
  "\u043D\u0430\u0434\u0456\u0441\u043B\u0430\u0442\u0438",
560
612
  "\u043D\u0430\u0434\u0456\u0448\u043B\u0438",
561
613
  "\u044F\u043A\u0449\u043E",
@@ -601,7 +653,6 @@ var HINDI_KEYWORDS = /* @__PURE__ */ new Set([
601
653
  ]);
602
654
  var BENGALI_KEYWORDS = /* @__PURE__ */ new Set([
603
655
  "\u099F\u0997\u09B2",
604
- "\u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09A8",
605
656
  "\u09AF\u09CB\u0997",
606
657
  "\u09AF\u09CB\u0997 \u0995\u09B0\u09C1\u09A8",
607
658
  "\u09B8\u09B0\u09BE\u09A8",
@@ -626,7 +677,6 @@ var BENGALI_KEYWORDS = /* @__PURE__ */ new Set([
626
677
  "\u09AA\u09C1\u09A8\u09B0\u09BE\u09AC\u09C3\u09A4\u09CD\u09A4\u09BF",
627
678
  "\u09AC\u09BE\u09B0 \u09AC\u09BE\u09B0",
628
679
  "\u0985\u09AA\u09C7\u0995\u09CD\u09B7\u09BE",
629
- "\u09A5\u09BE\u09AE\u09C1\u09A8",
630
680
  "\u09AF\u09A4\u0995\u09CD\u09B7\u09A3"
631
681
  ]);
632
682
  var THAI_KEYWORDS = /* @__PURE__ */ new Set([
@@ -667,11 +717,11 @@ var TL_KEYWORDS = /* @__PURE__ */ new Set([
667
717
  "taasan",
668
718
  "bawasan",
669
719
  "ibaba",
720
+ "palitawin",
670
721
  "magpatugtog",
671
722
  "ipadala",
672
723
  "magpadala",
673
724
  "kung",
674
- "kapag",
675
725
  "kung_hindi",
676
726
  "kundi",
677
727
  "ulitin",
@@ -687,6 +737,7 @@ var LANGUAGE_KEYWORDS = {
687
737
  ko: KOREAN_KEYWORDS,
688
738
  zh: CHINESE_KEYWORDS,
689
739
  ar: ARABIC_KEYWORDS,
740
+ he: HE_KEYWORDS,
690
741
  es: SPANISH_KEYWORDS,
691
742
  pt: PORTUGUESE_KEYWORDS,
692
743
  fr: FRENCH_KEYWORDS,
@@ -701,6 +752,7 @@ var LANGUAGE_KEYWORDS = {
701
752
  th: THAI_KEYWORDS,
702
753
  tr: TURKISH_KEYWORDS,
703
754
  id: INDONESIAN_KEYWORDS,
755
+ ms: MS_KEYWORDS,
704
756
  sw: SWAHILI_KEYWORDS,
705
757
  qu: QUECHUA_KEYWORDS,
706
758
  tl: TL_KEYWORDS
@@ -708,7 +760,18 @@ var LANGUAGE_KEYWORDS = {
708
760
  function containsLanguageKeywords(script, language) {
709
761
  const keywords = LANGUAGE_KEYWORDS[language];
710
762
  if (!keywords || keywords.size === 0) return false;
711
- const nonLatinLangs = ["ja", "ko", "zh", "ar", "ru", "uk", "hi", "bn", "th"];
763
+ const nonLatinLangs = [
764
+ "ja",
765
+ "ko",
766
+ "zh",
767
+ "ar",
768
+ "he",
769
+ "ru",
770
+ "uk",
771
+ "hi",
772
+ "bn",
773
+ "th"
774
+ ];
712
775
  if (nonLatinLangs.includes(language)) {
713
776
  for (const keyword of keywords) {
714
777
  if (script.includes(keyword)) {
@@ -792,6 +855,61 @@ function getOptimalRegion(languages) {
792
855
  return "all";
793
856
  }
794
857
 
858
+ // src/htmx-localized-attrs.ts
859
+ var HX_LIVE_LOCALIZED = /* @__PURE__ */ new Set([
860
+ // Each entry is the suffix after the `hx-` prefix.
861
+ "\u0645\u0628\u0627\u0634\u0631",
862
+ // ar
863
+ "\u09B2\u09BE\u0987\u09AD",
864
+ // bn
865
+ "direkt",
866
+ // de
867
+ "en-vivo",
868
+ // es
869
+ "en-direct",
870
+ // fr
871
+ "\u05D7\u05D9",
872
+ // he
873
+ "\u0932\u093E\u0907\u0935",
874
+ // hi
875
+ "langsung",
876
+ // id
877
+ "in-diretta",
878
+ // it
879
+ "\u30E9\u30A4\u30D6",
880
+ // ja
881
+ "\uC2E4\uC2DC\uAC04",
882
+ // ko
883
+ "langsung",
884
+ // ms
885
+ "na-\u017Cywo",
886
+ // pl
887
+ "ao-vivo",
888
+ // pt
889
+ "kawsachkaq",
890
+ // qu
891
+ "\u0432-\u043F\u0440\u044F\u043C\u043E\u043C-\u044D\u0444\u0438\u0440\u0435",
892
+ // ru
893
+ "moja-kwa-moja",
894
+ // sw
895
+ "\u0E44\u0E25\u0E1F\u0E4C",
896
+ // th
897
+ "canl\u0131",
898
+ // tr
899
+ "\u043D\u0430\u0436\u0438\u0432\u043E",
900
+ // uk
901
+ "tr\u1EF1c-ti\u1EBFp",
902
+ // vi
903
+ "\u5B9E\u65F6"
904
+ // zh
905
+ ]);
906
+ function buildLocalizedHxLivePattern() {
907
+ const suffixes = [...HX_LIVE_LOCALIZED].map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
908
+ return new RegExp(`\\bhx-(?:${suffixes})\\s*=\\s*["']`, "u");
909
+ }
910
+ var SSE_NS_PATTERN = /\bsse-[\w\-\p{L}]+\s*=\s*["']/u;
911
+ var WS_NS_PATTERN = /\bws-[\w\-\p{L}]+\s*=\s*["']/u;
912
+
795
913
  // src/scanner.ts
796
914
  var HTMX_REQUEST_PATTERN = /\b(hx-get|hx-post|hx-put|hx-patch|hx-delete)\s*=\s*["']([^"']+)["']/gi;
797
915
  var FIXI_ACTION_PATTERN = /\bfx-action\s*=\s*["']([^"']+)["']/gi;
@@ -802,6 +920,14 @@ var HTMX_TRIGGER_PATTERN = /\b(hx-trigger|fx-trigger)\s*=\s*["']([^"']+)["']/gi;
802
920
  var HTMX_URL_PATTERN = /\b(hx-push-url|hx-replace-url)\s*=\s*["'][^"']+["']/gi;
803
921
  var HTMX_CONFIRM_PATTERN = /\bhx-confirm\s*=\s*["']/gi;
804
922
  var HTMX_ON_PATTERN = /\bhx-on:(\w+)\s*=\s*["']([^"']+)["']/g;
923
+ var HX_LIVE_PATTERN = /\bhx-live\s*=\s*["']/i;
924
+ var SSE_CONNECT_PATTERN = /\bsse-connect\s*=\s*["']/i;
925
+ var SSE_SWAP_PATTERN = /\bsse-swap\s*=\s*["']/i;
926
+ var WS_CONNECT_PATTERN = /\bws-connect\s*=\s*["']/i;
927
+ var WS_SEND_PATTERN = /\bws-send(\s*=\s*["']|\b)/i;
928
+ var HX_LIVE_LOCALIZED_PATTERN = buildLocalizedHxLivePattern();
929
+ var LOCALIZED_HX_PATTERN = /\b(hx|sse|ws)-[\w\-\p{L}]+\s*=\s*["']/u;
930
+ var BIND_TO_PROPERTY_PATTERN = /\bbind\s+\S+\s+to\s+\S+\.\w+/i;
805
931
  function toRegex(pattern, defaultPattern) {
806
932
  if (!pattern) return defaultPattern;
807
933
  if (pattern instanceof RegExp) return pattern;
@@ -834,7 +960,9 @@ var Scanner = class {
834
960
  commands: /* @__PURE__ */ new Set(),
835
961
  blocks: /* @__PURE__ */ new Set(),
836
962
  positional: false,
837
- detectedLanguages: /* @__PURE__ */ new Set()
963
+ detectedLanguages: /* @__PURE__ */ new Set(),
964
+ needsReactivity: false,
965
+ needsBindToProperty: false
838
966
  };
839
967
  const attrPatterns = [
840
968
  /_\s*=\s*"([^"]+)"/g,
@@ -869,6 +997,7 @@ var Scanner = class {
869
997
  if (/hx-target\s*=\s*["'](closest|next|previous|find)\s/i.test(code)) {
870
998
  usage.positional = true;
871
999
  }
1000
+ if (htmxUsage.needsReactivity) usage.needsReactivity = true;
872
1001
  }
873
1002
  if (this.debug && (usage.commands.size > 0 || usage.blocks.size > 0 || usage.detectedLanguages.size > 0 || usage.htmx?.hasHtmxAttributes)) {
874
1003
  console.log(`[hyperfixi] Scanned ${id}:`, {
@@ -908,6 +1037,13 @@ var Scanner = class {
908
1037
  for (const lang of languages) {
909
1038
  usage.detectedLanguages.add(lang);
910
1039
  }
1040
+ if (/\blive\b/.test(script) || /\bwhen\s+\S+\s+changes\b/i.test(script) || /\bbind\b/.test(script) || /\^\w/.test(script)) {
1041
+ usage.needsReactivity = true;
1042
+ }
1043
+ if (BIND_TO_PROPERTY_PATTERN.test(script)) {
1044
+ usage.needsBindToProperty = true;
1045
+ usage.needsReactivity = true;
1046
+ }
911
1047
  }
912
1048
  /**
913
1049
  * Scan for htmx/fixi attributes
@@ -921,7 +1057,12 @@ var Scanner = class {
921
1057
  onHandlers: [],
922
1058
  triggerModifiers: /* @__PURE__ */ new Set(),
923
1059
  urlManagement: /* @__PURE__ */ new Set(),
924
- usesConfirm: false
1060
+ usesConfirm: false,
1061
+ needsHxLive: false,
1062
+ needsSSE: false,
1063
+ needsWS: false,
1064
+ needsBindToProperty: false,
1065
+ needsReactivity: false
925
1066
  };
926
1067
  let match;
927
1068
  const requestPattern = new RegExp(HTMX_REQUEST_PATTERN.source, "gi");
@@ -970,6 +1111,35 @@ var Scanner = class {
970
1111
  while (match = targetPattern.exec(code)) {
971
1112
  usage.hasHtmxAttributes = true;
972
1113
  }
1114
+ if (HX_LIVE_PATTERN.test(code)) {
1115
+ usage.hasHtmxAttributes = true;
1116
+ usage.needsHxLive = true;
1117
+ usage.needsReactivity = true;
1118
+ }
1119
+ if (SSE_CONNECT_PATTERN.test(code) || SSE_SWAP_PATTERN.test(code)) {
1120
+ usage.hasHtmxAttributes = true;
1121
+ usage.needsSSE = true;
1122
+ }
1123
+ if (WS_CONNECT_PATTERN.test(code) || WS_SEND_PATTERN.test(code)) {
1124
+ usage.hasHtmxAttributes = true;
1125
+ usage.needsWS = true;
1126
+ }
1127
+ if (HX_LIVE_LOCALIZED_PATTERN.test(code)) {
1128
+ usage.hasHtmxAttributes = true;
1129
+ usage.needsHxLive = true;
1130
+ usage.needsReactivity = true;
1131
+ }
1132
+ if (!usage.needsSSE && SSE_NS_PATTERN.test(code)) {
1133
+ usage.hasHtmxAttributes = true;
1134
+ usage.needsSSE = true;
1135
+ }
1136
+ if (!usage.needsWS && WS_NS_PATTERN.test(code)) {
1137
+ usage.hasHtmxAttributes = true;
1138
+ usage.needsWS = true;
1139
+ }
1140
+ if (!usage.hasHtmxAttributes && LOCALIZED_HX_PATTERN.test(code)) {
1141
+ usage.hasHtmxAttributes = true;
1142
+ }
973
1143
  return usage;
974
1144
  }
975
1145
  /**
@@ -1085,13 +1255,22 @@ var Aggregator = class {
1085
1255
  onHandlers: [],
1086
1256
  triggerModifiers: /* @__PURE__ */ new Set(),
1087
1257
  urlManagement: /* @__PURE__ */ new Set(),
1088
- usesConfirm: false
1258
+ usesConfirm: false,
1259
+ needsHxLive: false,
1260
+ needsSSE: false,
1261
+ needsWS: false,
1262
+ needsBindToProperty: false,
1263
+ needsReactivity: false
1089
1264
  };
1265
+ let needsReactivity = false;
1266
+ let needsBindToProperty = false;
1090
1267
  for (const usage of this.fileUsage.values()) {
1091
1268
  for (const cmd of usage.commands) commands.add(cmd);
1092
1269
  for (const block of usage.blocks) blocks.add(block);
1093
1270
  for (const lang of usage.detectedLanguages) detectedLanguages.add(lang);
1094
1271
  if (usage.positional) positional = true;
1272
+ if (usage.needsReactivity) needsReactivity = true;
1273
+ if (usage.needsBindToProperty) needsBindToProperty = true;
1095
1274
  if (usage.htmx) {
1096
1275
  if (usage.htmx.hasHtmxAttributes) htmx.hasHtmxAttributes = true;
1097
1276
  if (usage.htmx.hasFixiAttributes) htmx.hasFixiAttributes = true;
@@ -1101,6 +1280,14 @@ var Aggregator = class {
1101
1280
  for (const modifier of usage.htmx.triggerModifiers) htmx.triggerModifiers.add(modifier);
1102
1281
  for (const url of usage.htmx.urlManagement) htmx.urlManagement.add(url);
1103
1282
  if (usage.htmx.usesConfirm) htmx.usesConfirm = true;
1283
+ if (usage.htmx.needsHxLive) htmx.needsHxLive = true;
1284
+ if (usage.htmx.needsSSE) htmx.needsSSE = true;
1285
+ if (usage.htmx.needsWS) htmx.needsWS = true;
1286
+ if (usage.htmx.needsBindToProperty) htmx.needsBindToProperty = true;
1287
+ if (usage.htmx.needsReactivity) {
1288
+ htmx.needsReactivity = true;
1289
+ needsReactivity = true;
1290
+ }
1104
1291
  }
1105
1292
  }
1106
1293
  this.cachedUsage = {
@@ -1109,6 +1296,8 @@ var Aggregator = class {
1109
1296
  positional,
1110
1297
  detectedLanguages,
1111
1298
  htmx,
1299
+ needsReactivity,
1300
+ needsBindToProperty,
1112
1301
  fileUsage: new Map(this.fileUsage)
1113
1302
  };
1114
1303
  return this.cachedUsage;
@@ -1458,7 +1647,6 @@ var SINGLE_LANGUAGE_BUNDLES = [
1458
1647
  "de",
1459
1648
  "en",
1460
1649
  "es",
1461
- "es-MX",
1462
1650
  "fr",
1463
1651
  "he",
1464
1652
  "hi",
@@ -1511,7 +1699,6 @@ var SEMANTIC_BUNDLE_SIZES = {
1511
1699
  de: { raw: "57 KB", gzip: "~14 KB" },
1512
1700
  en: { raw: "82 KB", gzip: "~20 KB" },
1513
1701
  es: { raw: "64 KB", gzip: "~16 KB" },
1514
- "es-MX": { raw: "64 KB", gzip: "~16 KB" },
1515
1702
  fr: { raw: "57 KB", gzip: "~14 KB" },
1516
1703
  he: { raw: "56 KB", gzip: "~14 KB" },
1517
1704
  hi: { raw: "56 KB", gzip: "~14 KB" },
@@ -1547,7 +1734,6 @@ var LANGUAGES_WITH_ESM_EXPORTS = /* @__PURE__ */ new Set([
1547
1734
  "de",
1548
1735
  "en",
1549
1736
  "es",
1550
- "es-MX",
1551
1737
  "fr",
1552
1738
  "he",
1553
1739
  "hi",
@@ -1620,7 +1806,7 @@ function generateSemanticIntegrationCode(config) {
1620
1806
 
1621
1807
  // Core infrastructure (no language data bundled)
1622
1808
  import {
1623
- createSemanticAnalyzer,
1809
+ parseWithConfidence,
1624
1810
  buildAST,
1625
1811
  isLanguageSupported,
1626
1812
  } from '@lokascript/semantic/core';
@@ -1641,7 +1827,6 @@ const grammarTransformer = new GrammarTransformer();
1641
1827
  }
1642
1828
  const aliasesCode = generateMultilingualAliases(config.languages);
1643
1829
  code += `
1644
- const semanticAnalyzer = createSemanticAnalyzer();
1645
1830
  const SUPPORTED_SEMANTIC_LANGUAGES = ['${languages}', 'en'];
1646
1831
  const SEMANTIC_CONFIDENCE_THRESHOLD = 0.7;
1647
1832
 
@@ -1669,7 +1854,7 @@ function parseWithSemantic(code, lang = null) {
1669
1854
  for (const tryLang of languagesToTry) {
1670
1855
  if (!isLanguageSupported(tryLang)) continue;
1671
1856
  try {
1672
- const result = semanticAnalyzer.analyze(code, tryLang);
1857
+ const result = parseWithConfidence(code, tryLang);
1673
1858
  if (result && result.confidence >= SEMANTIC_CONFIDENCE_THRESHOLD) {
1674
1859
  // buildAST returns {ast, warnings} - extract the ast
1675
1860
  const buildResult = buildAST(result.node);
@@ -2153,6 +2338,28 @@ var Generator = class {
2153
2338
  if (commands.length === 0 && blocks.length === 0 && !positional && !semanticConfig.enabled) {
2154
2339
  return this.generateEmptyBundle(options);
2155
2340
  }
2341
+ const needsV4Bundle = usage.needsReactivity || usage.htmx?.needsHxLive || usage.htmx?.needsSSE || usage.htmx?.needsWS;
2342
+ if (needsV4Bundle) {
2343
+ if (this.debug) {
2344
+ const triggers = [];
2345
+ if (usage.htmx?.needsHxLive) triggers.push("hx-live");
2346
+ if (usage.htmx?.needsSSE) triggers.push("sse-connect/sse-swap");
2347
+ if (usage.htmx?.needsWS) triggers.push("ws-connect/ws-send");
2348
+ if (usage.needsBindToProperty) triggers.push("bind-to-property");
2349
+ else if (usage.needsReactivity) triggers.push("live/when/bind in _=");
2350
+ console.log(
2351
+ `[hyperfixi] Detected htmx v4 features: ${triggers.join(", ")}
2352
+ Using the hx-v4 premade bundle (auto-installs reactivity + SSE/WS).`
2353
+ );
2354
+ const onlyReactivity = usage.needsReactivity && !usage.htmx?.needsHxLive && !usage.htmx?.needsSSE && !usage.htmx?.needsWS && !usage.htmx?.hasHtmxAttributes;
2355
+ if (onlyReactivity) {
2356
+ console.log(
2357
+ ` Note: this project uses reactivity only (no hx-*/sse-*/ws-* attributes). A reactivity-only fallback is parked pending size feedback.`
2358
+ );
2359
+ }
2360
+ }
2361
+ return this.generateDevFallback("hx-v4");
2362
+ }
2156
2363
  const unsupportedCommands = this.getUnsupportedCommands(commands);
2157
2364
  if (unsupportedCommands.length > 0) {
2158
2365
  if (this.debug) {
@@ -2313,10 +2520,19 @@ export { api };
2313
2520
  `;
2314
2521
  }
2315
2522
  /**
2316
- * Generate a development fallback bundle
2523
+ * Generate a development fallback bundle.
2524
+ *
2525
+ * `hx-v4` re-exports the htmx v4 surface bundle: full runtime +
2526
+ * htmx-compat + reactivity (hx-live, bind, when) + SSE + WS, all
2527
+ * auto-installed. The vite plugin selects this when scanner detects
2528
+ * any of `hx-live`, `sse-connect`/`sse-swap`, `ws-connect`/`ws-send`,
2529
+ * or `bind ... to <expr>.<prop>` in `_=` bodies — the minimal
2530
+ * generator can't satisfy those features (the slim runtime lacks the
2531
+ * reactive notify chain and the SSE/WS modules). Larger artifact but
2532
+ * correct end-to-end.
2317
2533
  */
2318
2534
  generateDevFallback(fallback) {
2319
- const bundle = fallback === "full" ? "@hyperfixi/core/browser" : "@hyperfixi/core/browser/hybrid-complete";
2535
+ const bundle = fallback === "full" ? "@hyperfixi/core/browser" : fallback === "hx-v4" ? "@hyperfixi/core/browser/hybrid-hx-v4" : "@hyperfixi/core/browser/hybrid-complete";
2320
2536
  return `/**
2321
2537
  * LokaScript Dev Fallback Bundle
2322
2538
  *
@@ -2334,18 +2550,21 @@ export { default } from '${bundle}';
2334
2550
 
2335
2551
  // src/compiler.ts
2336
2552
  import { HybridParser } from "@hyperfixi/core/parser/hybrid/parser-core";
2337
- var semanticAnalyzer = null;
2553
+ var semanticParse = null;
2554
+ var supportsLanguage = null;
2338
2555
  var buildASTFn = null;
2339
- function setSemanticParser(analyzer, buildAST) {
2340
- semanticAnalyzer = analyzer;
2556
+ function setSemanticParser(parse, supports, buildAST) {
2557
+ semanticParse = parse;
2558
+ supportsLanguage = supports;
2341
2559
  buildASTFn = buildAST;
2342
2560
  }
2343
2561
  function clearSemanticParser() {
2344
- semanticAnalyzer = null;
2562
+ semanticParse = null;
2563
+ supportsLanguage = null;
2345
2564
  buildASTFn = null;
2346
2565
  }
2347
2566
  function hasSemanticParser() {
2348
- return semanticAnalyzer !== null && buildASTFn !== null;
2567
+ return semanticParse !== null && supportsLanguage !== null && buildASTFn !== null;
2349
2568
  }
2350
2569
  var SEMANTIC_CONFIDENCE_THRESHOLD = 0.7;
2351
2570
  var multilingualAliases = {};
@@ -2400,9 +2619,9 @@ function compile(script, options = {}) {
2400
2619
  const { language = "en", debug = false } = options;
2401
2620
  try {
2402
2621
  let ast;
2403
- if (language !== "en" && semanticAnalyzer && buildASTFn) {
2404
- if (semanticAnalyzer.supportsLanguage(language)) {
2405
- const result = semanticAnalyzer.analyze(script, language);
2622
+ if (language !== "en" && semanticParse && supportsLanguage && buildASTFn) {
2623
+ if (supportsLanguage(language)) {
2624
+ const result = semanticParse(script, language);
2406
2625
  if (result.node && result.confidence >= SEMANTIC_CONFIDENCE_THRESHOLD) {
2407
2626
  const { ast: builtAST, warnings } = buildASTFn(result.node);
2408
2627
  if (debug && warnings.length > 0) {
@@ -2416,7 +2635,7 @@ function compile(script, options = {}) {
2416
2635
  }
2417
2636
  } else {
2418
2637
  if (debug) {
2419
- const reason = result.errors?.join(", ") || `low confidence (${result.confidence.toFixed(2)})`;
2638
+ const reason = result.error || `low confidence (${result.confidence.toFixed(2)})`;
2420
2639
  console.log(`[hyperfixi] Semantic parse failed for "${script}": ${reason}`);
2421
2640
  }
2422
2641
  return null;