gofront 1.6.0 → 1.7.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.
Files changed (80) hide show
  1. package/README.md +35 -39
  2. package/package.json +37 -4
  3. package/src/backend/js/expressions.js +559 -144
  4. package/src/backend/js/index.js +174 -49
  5. package/src/backend/js/runtime.js +1518 -54
  6. package/src/backend/js/statements.js +91 -73
  7. package/src/backend/js/stdlib/base64.js +12 -0
  8. package/src/backend/js/stdlib/binary.js +102 -0
  9. package/src/backend/js/stdlib/bits.js +15 -1
  10. package/src/backend/js/stdlib/builder.js +28 -9
  11. package/src/backend/js/stdlib/bytes.js +75 -18
  12. package/src/backend/js/stdlib/errors.js +25 -1
  13. package/src/backend/js/stdlib/fmt.js +70 -13
  14. package/src/backend/js/stdlib/hex.js +17 -0
  15. package/src/backend/js/stdlib/html.js +1 -1
  16. package/src/backend/js/stdlib/index.js +28 -0
  17. package/src/backend/js/stdlib/io.js +105 -3
  18. package/src/backend/js/stdlib/json.js +972 -0
  19. package/src/backend/js/stdlib/math.js +13 -1
  20. package/src/backend/js/stdlib/path.js +18 -4
  21. package/src/backend/js/stdlib/rand.js +10 -5
  22. package/src/backend/js/stdlib/sha256.js +12 -0
  23. package/src/backend/js/stdlib/simd.js +24 -0
  24. package/src/backend/js/stdlib/slices.js +52 -26
  25. package/src/backend/js/stdlib/strconv.js +21 -5
  26. package/src/backend/js/stdlib/strings.js +148 -15
  27. package/src/backend/js/stdlib/sync.js +13 -0
  28. package/src/backend/js/stdlib/time.js +90 -19
  29. package/src/backend/js/stdlib/unicode.js +2 -2
  30. package/src/backend/wasm/boundary.js +1225 -41
  31. package/src/backend/wasm/const-fold.js +205 -0
  32. package/src/backend/wasm/emit-builtins.js +220 -76
  33. package/src/backend/wasm/emit-exprs.js +475 -276
  34. package/src/backend/wasm/emit-maps.js +144 -8
  35. package/src/backend/wasm/emit-stdlib.js +12 -3005
  36. package/src/backend/wasm/emit-stmts.js +264 -270
  37. package/src/backend/wasm/emit.js +124 -87
  38. package/src/backend/wasm/encode.js +140 -29
  39. package/src/backend/wasm/index.js +1001 -32
  40. package/src/backend/wasm/optimize.js +17 -6
  41. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  42. package/src/backend/wasm/stdlib/core.js +8903 -0
  43. package/src/backend/wasm/stdlib/index.js +43 -0
  44. package/src/backend/wasm/stdlib/io.js +2161 -0
  45. package/src/backend/wasm/stdlib/packages.js +2400 -0
  46. package/src/backend/wasm/stdlib/pure.js +720 -0
  47. package/src/backend/wasm/stdlib/simd.js +234 -0
  48. package/src/backend/wasm/stdlib/time.js +1200 -0
  49. package/src/backend/wasm/types.js +174 -1
  50. package/src/backend/wasm/wat.js +37 -0
  51. package/src/cli-core.js +1 -1
  52. package/src/compiler.js +18 -4
  53. package/src/dts-parser.js +150 -140
  54. package/src/index.js +2 -6
  55. package/src/lexer.js +80 -29
  56. package/src/lower/boxing.js +1 -7
  57. package/src/lower/captures.js +2 -6
  58. package/src/lower/embedding.js +2 -1
  59. package/src/lower/ownership.js +30 -9
  60. package/src/minifier.js +20 -68
  61. package/src/parser/declarations.js +3 -6
  62. package/src/parser/expressions.js +3 -8
  63. package/src/parser/index.js +18 -3
  64. package/src/parser/statements.js +25 -38
  65. package/src/parser/struct-tag.js +144 -0
  66. package/src/parser/types.js +64 -8
  67. package/src/resolver.js +2 -27
  68. package/src/templ-lexer.js +29 -63
  69. package/src/templ-parser.js +22 -45
  70. package/src/typechecker/assignability.js +126 -13
  71. package/src/typechecker/expressions.js +168 -16
  72. package/src/typechecker/index.js +35 -8
  73. package/src/typechecker/resolve.js +187 -8
  74. package/src/typechecker/statements.js +68 -9
  75. package/src/typechecker/stdlib/core.js +280 -89
  76. package/src/typechecker/stdlib/extended.js +490 -1
  77. package/src/typechecker/stdlib/simd.js +73 -0
  78. package/src/typechecker/stdlib.js +2 -0
  79. package/src/typechecker/types.js +362 -9
  80. package/src/vendor.js +86 -11
@@ -83,6 +83,23 @@ export const BROWSER_GLOBALS = new Set([
83
83
  "prompt",
84
84
  "localStorage",
85
85
  "sessionStorage",
86
+ "WebSocket",
87
+ "Worker",
88
+ "HTMLElement",
89
+ "Element",
90
+ "Event",
91
+ "CustomEvent",
92
+ "URL",
93
+ "URLSearchParams",
94
+ "FormData",
95
+ "Headers",
96
+ "Request",
97
+ "Response",
98
+ "Blob",
99
+ "File",
100
+ "FileReader",
101
+ "TextEncoder",
102
+ "TextDecoder",
86
103
  ]);
87
104
 
88
105
  // Stdlib packages the WASM backend can emit. Extend as src/backend/wasm/emit-stdlib.js
@@ -91,22 +108,46 @@ export const BROWSER_GLOBALS = new Set([
91
108
  export const WASM_SUPPORTED_STDLIB = new Set([
92
109
  "math",
93
110
  "math/bits",
111
+ "math/rand",
94
112
  "errors",
95
113
  "fmt",
96
114
  "strings",
115
+ "bytes",
97
116
  "strconv",
117
+ "unicode",
98
118
  "unicode/utf8",
99
119
  "slices",
100
120
  "maps",
101
121
  "sort",
122
+ "path",
123
+ "path/filepath",
124
+ "html",
125
+ "io",
126
+ "time",
102
127
  "testing",
103
128
  "gofront/shared",
129
+ "gofront/simd",
130
+ "sync",
131
+ "crypto/rand",
132
+ "crypto/sha256",
133
+ "encoding/binary",
134
+ "encoding/hex",
135
+ "encoding/base64",
136
+ ]);
137
+
138
+ export const STDLIB_PACKAGES = new Set([
139
+ ...WASM_SUPPORTED_STDLIB,
140
+ "os",
141
+ "gom",
142
+ "regexp",
143
+ "encoding/json",
104
144
  ]);
105
145
 
106
146
  // Per-namespace member subset the WASM backend implements. `null` means every
107
147
  // member the checker knows about. Members outside the set are rejected in
108
148
  // wasm/both packages with "'strings.Split' is not yet available in wasm packages".
109
149
  export const WASM_STDLIB_MEMBERS = new Map([
150
+ ["json", new Set()],
110
151
  [
111
152
  "math",
112
153
  new Set([
@@ -141,6 +182,14 @@ export const WASM_STDLIB_MEMBERS = new Map([
141
182
  "Log2",
142
183
  "Log10",
143
184
  "Round",
185
+ "Remainder",
186
+ "Float64bits",
187
+ "Float64frombits",
188
+ "Float32bits",
189
+ "Float32frombits",
190
+ "Sinh",
191
+ "Cosh",
192
+ "Tanh",
144
193
  // constants
145
194
  "E",
146
195
  "Pi",
@@ -185,9 +234,16 @@ export const WASM_STDLIB_MEMBERS = new Map([
185
234
  "OnesCount64",
186
235
  "RotateLeft32",
187
236
  "RotateLeft64",
237
+ "ReverseBytes16",
238
+ "ReverseBytes32",
239
+ "ReverseBytes64",
240
+ "Len32",
241
+ "Len64",
242
+ "Reverse32",
243
+ "Reverse64",
188
244
  ]),
189
245
  ],
190
- ["errors", new Set(["New", "Is", "Unwrap"])],
246
+ ["errors", new Set(["New", "Is", "Unwrap", "As", "Join"])],
191
247
  [
192
248
  "utf8",
193
249
  new Set([
@@ -218,7 +274,22 @@ export const WASM_STDLIB_MEMBERS = new Map([
218
274
  "StringsAreSorted",
219
275
  ]),
220
276
  ],
221
- ["fmt", new Set(["Sprintf", "Errorf", "Printf", "Println", "Print"])],
277
+ [
278
+ "fmt",
279
+ new Set([
280
+ "Sprintf",
281
+ "Errorf",
282
+ "Printf",
283
+ "Println",
284
+ "Print",
285
+ "Fprintf",
286
+ "Fprintln",
287
+ "Fprint",
288
+ "Sscan",
289
+ "Sscanln",
290
+ "Sscanf",
291
+ ]),
292
+ ],
222
293
  [
223
294
  "strings",
224
295
  new Set([
@@ -251,6 +322,24 @@ export const WASM_STDLIB_MEMBERS = new Map([
251
322
  "Split",
252
323
  "Fields",
253
324
  "Join",
325
+ "Cut",
326
+ "CutPrefix",
327
+ "CutSuffix",
328
+ "SplitN",
329
+ "SplitAfter",
330
+ "SplitAfterN",
331
+ "LastIndexAny",
332
+ "Map",
333
+ "IndexFunc",
334
+ "LastIndexFunc",
335
+ "TrimFunc",
336
+ "TrimLeftFunc",
337
+ "TrimRightFunc",
338
+ "FieldsFunc",
339
+ "NewReplacer",
340
+ "Builder",
341
+ "NewReader",
342
+ "Reader",
254
343
  ]),
255
344
  ],
256
345
  [
@@ -259,17 +348,55 @@ export const WASM_STDLIB_MEMBERS = new Map([
259
348
  "Itoa",
260
349
  "Atoi",
261
350
  "FormatInt",
351
+ "FormatUint",
262
352
  "FormatFloat",
263
353
  "FormatBool",
264
354
  "ParseInt",
355
+ "ParseUint",
265
356
  "ParseFloat",
266
357
  "ParseBool",
267
358
  "Quote",
359
+ "Unquote",
360
+ "AppendInt",
361
+ "AppendUint",
362
+ "AppendFloat",
363
+ "AppendBool",
364
+ "ErrSyntax",
365
+ "ErrRange",
268
366
  ]),
269
367
  ],
270
368
  [
271
369
  "slices",
272
- new Set(["Sort", "Reverse", "Contains", "Index", "Equal", "Clone"]),
370
+ new Set([
371
+ "Sort",
372
+ "Reverse",
373
+ "Contains",
374
+ "Index",
375
+ "IndexFunc",
376
+ "ContainsFunc",
377
+ "BinarySearch",
378
+ "BinarySearchFunc",
379
+ "Equal",
380
+ "Clone",
381
+ "Compare",
382
+ "SortFunc",
383
+ "SortStableFunc",
384
+ "IsSorted",
385
+ "IsSortedFunc",
386
+ "Min",
387
+ "Max",
388
+ "MinFunc",
389
+ "MaxFunc",
390
+ "Compact",
391
+ "CompactFunc",
392
+ "Concat",
393
+ "Delete",
394
+ "DeleteFunc",
395
+ "Insert",
396
+ "Replace",
397
+ "Grow",
398
+ "Clip",
399
+ ]),
273
400
  ],
274
401
  [
275
402
  "maps",
@@ -284,6 +411,216 @@ export const WASM_STDLIB_MEMBERS = new Map([
284
411
  "DeleteFunc",
285
412
  ]),
286
413
  ],
414
+ [
415
+ "unicode",
416
+ new Set([
417
+ "IsLetter",
418
+ "IsDigit",
419
+ "IsSpace",
420
+ "IsUpper",
421
+ "IsLower",
422
+ "IsPunct",
423
+ "IsControl",
424
+ "IsPrint",
425
+ "IsGraphic",
426
+ "ToUpper",
427
+ "ToLower",
428
+ ]),
429
+ ],
430
+ [
431
+ "rand",
432
+ new Set([
433
+ "Intn",
434
+ "Float64",
435
+ "Float32",
436
+ "Int",
437
+ "Int63",
438
+ "Int63n",
439
+ "Int31",
440
+ "Int31n",
441
+ "Seed",
442
+ "Shuffle",
443
+ "Perm",
444
+ "Read",
445
+ "Reader",
446
+ ]),
447
+ ],
448
+ [
449
+ "bytes",
450
+ new Set([
451
+ "Contains",
452
+ "HasPrefix",
453
+ "HasSuffix",
454
+ "Index",
455
+ "Count",
456
+ "Repeat",
457
+ "Replace",
458
+ "ReplaceAll",
459
+ "ToUpper",
460
+ "ToLower",
461
+ "TrimSpace",
462
+ "Trim",
463
+ "Equal",
464
+ "Split",
465
+ "Join",
466
+ "TrimPrefix",
467
+ "TrimSuffix",
468
+ "TrimLeft",
469
+ "TrimRight",
470
+ "TrimFunc",
471
+ "IndexByte",
472
+ "LastIndex",
473
+ "LastIndexByte",
474
+ "Fields",
475
+ "FieldsFunc",
476
+ "Clone",
477
+ "Cut",
478
+ "ToTitle",
479
+ "ContainsAny",
480
+ "ContainsRune",
481
+ "Map",
482
+ "SplitN",
483
+ "Buffer",
484
+ "NewReader",
485
+ "Reader",
486
+ ]),
487
+ ],
488
+ [
489
+ "path",
490
+ new Set([
491
+ "Base",
492
+ "Dir",
493
+ "Ext",
494
+ "Join",
495
+ "Clean",
496
+ "IsAbs",
497
+ "Split",
498
+ "Match",
499
+ "ErrBadPattern",
500
+ ]),
501
+ ],
502
+ [
503
+ "filepath",
504
+ new Set([
505
+ "Base",
506
+ "Dir",
507
+ "Ext",
508
+ "Join",
509
+ "Clean",
510
+ "IsAbs",
511
+ "Split",
512
+ "Match",
513
+ "ErrBadPattern",
514
+ ]),
515
+ ],
516
+ ["html", new Set(["EscapeString", "UnescapeString"])],
517
+ [
518
+ "io",
519
+ new Set([
520
+ "Writer",
521
+ "Reader",
522
+ "ReadWriter",
523
+ "Closer",
524
+ "EOF",
525
+ "ErrUnexpectedEOF",
526
+ "ErrShortWrite",
527
+ "ReadFull",
528
+ "ReadAll",
529
+ "Discard",
530
+ "WriteString",
531
+ "LimitReader",
532
+ "Copy",
533
+ "CopyN",
534
+ ]),
535
+ ],
536
+ [
537
+ "time",
538
+ new Set([
539
+ "Now",
540
+ "Since",
541
+ "Parse",
542
+ "Unix",
543
+ "UnixMicro",
544
+ "UnixMilli",
545
+ "Date",
546
+ "Duration",
547
+ "Nanosecond",
548
+ "Microsecond",
549
+ "Millisecond",
550
+ "Second",
551
+ "Minute",
552
+ "Hour",
553
+ "RFC3339",
554
+ "RFC3339Nano",
555
+ "DateOnly",
556
+ "TimeOnly",
557
+ "DateTime",
558
+ "UTC",
559
+ "Local",
560
+ "January",
561
+ "February",
562
+ "March",
563
+ "April",
564
+ "May",
565
+ "June",
566
+ "July",
567
+ "August",
568
+ "September",
569
+ "October",
570
+ "November",
571
+ "December",
572
+ "Sunday",
573
+ "Monday",
574
+ "Tuesday",
575
+ "Wednesday",
576
+ "Thursday",
577
+ "Friday",
578
+ "Saturday",
579
+ "Time",
580
+ ]),
581
+ ],
582
+ ["testing", new Set(["T"])],
583
+ ["sync", new Set(["Mutex", "RWMutex", "Once", "WaitGroup", "Locker"])],
584
+ ["sha256", new Set(["Sum256", "New", "Size", "BlockSize"])],
585
+ [
586
+ "binary",
587
+ new Set([
588
+ "LittleEndian",
589
+ "BigEndian",
590
+ "ByteOrder",
591
+ "Read",
592
+ "Write",
593
+ "Size",
594
+ "AppendUint16",
595
+ "AppendUint32",
596
+ "AppendUint64",
597
+ ]),
598
+ ],
599
+ [
600
+ "hex",
601
+ new Set([
602
+ "EncodeToString",
603
+ "DecodeString",
604
+ "Encode",
605
+ "Decode",
606
+ "EncodedLen",
607
+ "DecodedLen",
608
+ "ErrLength",
609
+ ]),
610
+ ],
611
+ [
612
+ "base64",
613
+ new Set([
614
+ "StdEncoding",
615
+ "URLEncoding",
616
+ "RawStdEncoding",
617
+ "RawURLEncoding",
618
+ "Encoding",
619
+ "NewEncoding",
620
+ "StdPadding",
621
+ "NoPadding",
622
+ ]),
623
+ ],
287
624
  ]);
288
625
 
289
626
  export const VALID_TARGETS = new Set(["js", "wasm", "both"]);
@@ -298,7 +635,7 @@ export const INT = { kind: "basic", name: "int" };
298
635
  export const INT8 = { kind: "basic", name: "int8" };
299
636
  export const INT16 = { kind: "basic", name: "int16" };
300
637
  export const INT32 = { kind: "basic", name: "int32" };
301
- const INT64 = { kind: "basic", name: "int64" };
638
+ export const INT64 = { kind: "basic", name: "int64" };
302
639
  export const UINT = { kind: "basic", name: "uint" };
303
640
  export const UINT8 = { kind: "basic", name: "uint8" };
304
641
  export const BYTE = UINT8;
@@ -498,17 +835,26 @@ export function typeStr(t) {
498
835
  case "map":
499
836
  return `map[${typeStr(t.key)}]${typeStr(t.value)}`;
500
837
  case "struct":
501
- return t.name || "struct{...}";
838
+ return t.typeArgs && t.typeArgs.length > 0
839
+ ? `${t.name || "struct"}[${t.typeArgs.map(typeStr).join(", ")}]`
840
+ : t.name || "struct{...}";
502
841
  case "interface":
503
842
  return t.name || "interface{...}";
504
843
  case "namespace":
505
844
  return t.name || "namespace{...}";
506
- case "func":
507
- return `func(${t.params.map(typeStr).join(", ")}) ${typeStr(t.returns[0] ?? VOID)}`;
845
+ case "func": {
846
+ const retStr =
847
+ t.returns.length > 1
848
+ ? `(${t.returns.map(typeStr).join(", ")})`
849
+ : typeStr(t.returns[0] ?? VOID);
850
+ return `func(${t.params.map(typeStr).join(", ")}) ${retStr}`;
851
+ }
508
852
  case "tuple":
509
853
  return `(${t.types.map(typeStr).join(", ")})`;
510
854
  case "named":
511
- return t.name;
855
+ return t.typeArgs && t.typeArgs.length > 0
856
+ ? `${t.name}[${t.typeArgs.map(typeStr).join(", ")}]`
857
+ : t.name;
512
858
  case "pointer":
513
859
  return `*${typeStr(t.base)}`;
514
860
  case "typeParam":
@@ -549,6 +895,7 @@ export class Scope {
549
895
  this.parent = parent;
550
896
  this.symbols = new Map();
551
897
  this._consts = new Set(); // names declared as const in this scope
898
+ this._constExprs = new Map(); // name -> AST initializer expr
552
899
  this._locals = new Set(); // names declared as local variables (var / :=)
553
900
  this._used = new Set(); // names referenced in this scope
554
901
  }
@@ -559,9 +906,15 @@ export class Scope {
559
906
  this.symbols.set(name, type);
560
907
  if (name !== "_") this._locals.add(name);
561
908
  }
562
- defineConst(name, type) {
909
+ defineConst(name, type, expr = null) {
563
910
  this.symbols.set(name, type);
564
911
  this._consts.add(name);
912
+ if (expr) this._constExprs.set(name, expr);
913
+ }
914
+ lookupConstExpr(name) {
915
+ if (this._constExprs.has(name)) return this._constExprs.get(name);
916
+ if (this.parent) return this.parent.lookupConstExpr(name);
917
+ return null;
565
918
  }
566
919
  isConst(name) {
567
920
  if (this._consts.has(name)) return true;
package/src/vendor.js CHANGED
@@ -19,12 +19,17 @@ export function getExportNames(pkgName, globals = {}) {
19
19
  const clean = base.replace(/[^a-zA-Z0-9_]/g, "_");
20
20
  const names = new Set([pkgName, base, clean]);
21
21
  const extra = globals[pkgName];
22
- for (const name of Array.isArray(extra) ? extra : extra ? [extra] : [])
23
- names.add(name);
22
+ for (const name of Array.isArray(extra)
23
+ ? extra
24
+ : typeof extra === "string"
25
+ ? [extra]
26
+ : []) {
27
+ if (typeof name === "string") names.add(name);
28
+ }
24
29
  return [...names];
25
30
  }
26
31
 
27
- export function generateVendorEntry(packages, globals = {}) {
32
+ export function generateVendorEntry(packages, globals = {}, namedExports = {}) {
28
33
  const imports = [];
29
34
  const assignments = [];
30
35
  const exports = [];
@@ -40,6 +45,46 @@ export function generateVendorEntry(packages, globals = {}) {
40
45
  );
41
46
  }
42
47
 
48
+ // Named export global assignments (e.g. window.h = preact.h)
49
+ const pkgNamed = Object.create(null);
50
+ const rawNamed = namedExports[pkg];
51
+ const isSafeKey = (k) =>
52
+ typeof k === "string" &&
53
+ k !== "__proto__" &&
54
+ k !== "prototype" &&
55
+ k !== "constructor";
56
+
57
+ if (Array.isArray(rawNamed)) {
58
+ for (const name of rawNamed) {
59
+ if (isSafeKey(name)) pkgNamed[name] = name;
60
+ }
61
+ } else if (rawNamed && typeof rawNamed === "object") {
62
+ for (const [k, v] of Object.entries(rawNamed)) {
63
+ if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
64
+ }
65
+ }
66
+
67
+ if (typeof globals[pkg] === "object" && !Array.isArray(globals[pkg])) {
68
+ for (const [k, v] of Object.entries(globals[pkg])) {
69
+ if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
70
+ }
71
+ } else if (Array.isArray(globals[pkg])) {
72
+ for (const item of globals[pkg]) {
73
+ if (typeof item === "object" && item !== null) {
74
+ for (const [k, v] of Object.entries(item)) {
75
+ if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ for (const [globalName, exportName] of Object.entries(pkgNamed)) {
82
+ if (!isSafeKey(globalName) || !isSafeKey(exportName)) continue;
83
+ assignments.push(
84
+ ` window[${JSON.stringify(globalName)}] = ${id}[${JSON.stringify(exportName)}] !== undefined ? ${id}[${JSON.stringify(exportName)}] : (${id}.default ? ${id}.default[${JSON.stringify(exportName)}] : undefined);`,
85
+ );
86
+ }
87
+
43
88
  const cleanId = pkg
44
89
  .replace(/^@[^/]+\//, "")
45
90
  .replace(/[^a-zA-Z0-9_$]/g, "_");
@@ -82,8 +127,26 @@ function findBundler(projectDir) {
82
127
  return null;
83
128
  }
84
129
 
85
- // Normalises `vendor.globals` ({ pkg: string | string[] }) to { pkg: string[] }.
86
- function normalizeGlobals(raw) {
130
+ export function normalizeNamedExports(raw) {
131
+ if (raw === undefined || raw === null) return {};
132
+ if (typeof raw !== "object" || Array.isArray(raw)) return {};
133
+ const out = {};
134
+ for (const [pkg, value] of Object.entries(raw)) {
135
+ if (Array.isArray(value)) {
136
+ const map = {};
137
+ for (const name of value) {
138
+ if (typeof name === "string") map[name] = name;
139
+ }
140
+ out[pkg] = map;
141
+ } else if (typeof value === "object" && value !== null) {
142
+ out[pkg] = { ...value };
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+
148
+ // Normalises `vendor.globals` ({ pkg: string | string[] | object })
149
+ export function normalizeGlobals(raw) {
87
150
  if (raw === undefined || raw === null) return {};
88
151
  if (typeof raw !== "object" || Array.isArray(raw)) {
89
152
  throw new Error(
@@ -92,13 +155,17 @@ function normalizeGlobals(raw) {
92
155
  }
93
156
  const out = {};
94
157
  for (const [pkg, value] of Object.entries(raw)) {
95
- const list = Array.isArray(value) ? value : [value];
96
- if (list.some((v) => typeof v !== "string")) {
158
+ if (typeof value === "string") {
159
+ out[pkg] = [value];
160
+ } else if (Array.isArray(value)) {
161
+ out[pkg] = value;
162
+ } else if (typeof value === "object" && value !== null) {
163
+ out[pkg] = value;
164
+ } else {
97
165
  throw new Error(
98
- `gofront: vendor.globals[${JSON.stringify(pkg)}] must be a string or array of strings`,
166
+ `gofront: vendor.globals[${JSON.stringify(pkg)}] must be a string, array, or object`,
99
167
  );
100
168
  }
101
- out[pkg] = list;
102
169
  }
103
170
  return out;
104
171
  }
@@ -119,6 +186,7 @@ export function loadVendorConfig(projectDir) {
119
186
  let packages = dependencies;
120
187
  let minify = false;
121
188
  let globals = {};
189
+ let namedExports = {};
122
190
 
123
191
  if (typeof vendorConfig === "string") {
124
192
  dest = vendorConfig;
@@ -127,13 +195,16 @@ export function loadVendorConfig(projectDir) {
127
195
  if (Array.isArray(vendorConfig.packages)) packages = vendorConfig.packages;
128
196
  if (typeof vendorConfig.minify === "boolean") minify = vendorConfig.minify;
129
197
  globals = normalizeGlobals(vendorConfig.globals);
198
+ namedExports = normalizeNamedExports(
199
+ vendorConfig.namedExports ?? vendorConfig.exports,
200
+ );
130
201
  }
131
202
 
132
203
  if (!dest) {
133
204
  dest = existsSync(join(projectDir, "app")) ? "app/vendor.js" : "vendor.js";
134
205
  }
135
206
 
136
- return { dest, packages, minify, globals };
207
+ return { dest, packages, minify, globals, namedExports };
137
208
  }
138
209
 
139
210
  function resolveDestinationPaths(projectRoot, dest) {
@@ -299,6 +370,10 @@ export async function bundleVendor(projectDir = ".", options = {}) {
299
370
  const dest = options.dest ?? config.dest;
300
371
  const minify = options.minify ?? config.minify ?? false;
301
372
  const globals = { ...config.globals, ...normalizeGlobals(options.globals) };
373
+ const namedExports = {
374
+ ...config.namedExports,
375
+ ...normalizeNamedExports(options.namedExports ?? options.exports),
376
+ };
302
377
 
303
378
  if (!packages || packages.length === 0) {
304
379
  return {
@@ -366,7 +441,7 @@ export async function bundleVendor(projectDir = ".", options = {}) {
366
441
  };
367
442
  }
368
443
 
369
- const entryCode = generateVendorEntry(packages, globals);
444
+ const entryCode = generateVendorEntry(packages, globals, namedExports);
370
445
  const entryFile = join(
371
446
  projectRoot,
372
447
  `.gofront-vendor-entry-${Date.now()}.mjs`,