@vercel/microfrontends 2.2.1 → 2.2.2

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 (59) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +483 -394
  4. package/dist/config.cjs +191 -169
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +3 -2
  7. package/dist/config.js +192 -170
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +583 -511
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +589 -517
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +605 -533
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +614 -542
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +601 -529
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +607 -535
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +101 -50
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +4 -4
  25. package/dist/microfrontends/utils.js +102 -51
  26. package/dist/microfrontends/utils.js.map +1 -1
  27. package/dist/next/client.cjs +1 -1
  28. package/dist/next/client.cjs.map +1 -1
  29. package/dist/next/client.d.ts +8 -8
  30. package/dist/next/client.js +1 -1
  31. package/dist/next/client.js.map +1 -1
  32. package/dist/next/config.cjs +723 -647
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +720 -644
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +244 -222
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +245 -223
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +192 -170
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +1 -1
  43. package/dist/next/testing.js +193 -171
  44. package/dist/next/testing.js.map +1 -1
  45. package/dist/overrides.cjs +5 -5
  46. package/dist/overrides.cjs.map +1 -1
  47. package/dist/overrides.d.ts +9 -9
  48. package/dist/overrides.js +5 -5
  49. package/dist/overrides.js.map +1 -1
  50. package/dist/utils/mfe-port.cjs +620 -533
  51. package/dist/utils/mfe-port.cjs.map +1 -1
  52. package/dist/utils/mfe-port.d.ts +9 -1
  53. package/dist/utils/mfe-port.js +632 -546
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +8 -24
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.js +8 -24
  58. package/dist/validation.js.map +1 -1
  59. package/package.json +4 -6
package/dist/bin/cli.cjs CHANGED
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/bin/index.ts
27
- var import_commander = require("commander");
28
27
  var import_env = require("@next/env");
28
+ var import_commander = require("commander");
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@vercel/microfrontends",
33
- version: "2.2.1",
33
+ version: "2.2.2",
34
34
  private: false,
35
35
  description: "Defines configuration and utilities for microfrontends development",
36
36
  keywords: [
@@ -161,15 +161,14 @@ var package_default = {
161
161
  postbuild: "pnpm generate:exports",
162
162
  "generate:exports": "tsx scripts/generate-exports/index.ts",
163
163
  "generate:schema": "tsx scripts/generate-json-schema.ts",
164
- lint: "eslint .",
165
- "lint-fix": "eslint . --fix",
164
+ lint: "biome check",
166
165
  prepublishOnly: "pnpm build && pnpm generate:exports && pnpm generate:schema",
167
166
  proxy: "tsx src/proxy/index.ts",
168
167
  test: "cross-env TZ=UTC jest",
169
168
  typecheck: "tsc --noEmit"
170
169
  },
171
170
  dependencies: {
172
- "@next/env": "15.5.4",
171
+ "@next/env": "16.0.10",
173
172
  "@types/md5": "^2.3.5",
174
173
  ajv: "^8.17.1",
175
174
  commander: "^12.1.0",
@@ -195,10 +194,9 @@ var package_default = {
195
194
  "@types/react": "18.3.1",
196
195
  "@types/react-dom": "18.3.0",
197
196
  "@types/semver": "^7.7.0",
198
- "eslint-config-custom": "workspace:*",
199
197
  jest: "^29.7.0",
200
198
  "jest-environment-jsdom": "29.2.2",
201
- next: "15.5.4",
199
+ next: "16.0.10",
202
200
  react: "19.0.0",
203
201
  "react-dom": "19.0.0",
204
202
  "ts-config": "workspace:*",
@@ -249,11 +247,34 @@ var http = __toESM(require("http"), 1);
249
247
  var https = __toESM(require("https"), 1);
250
248
  var import_node_url = require("url");
251
249
  var import_cookie = require("cookie");
252
- var import_path_to_regexp3 = require("path-to-regexp");
253
250
  var import_http_proxy = __toESM(require("http-proxy"), 1);
251
+ var import_path_to_regexp3 = require("path-to-regexp");
254
252
 
255
- // src/config/microfrontends-config/isomorphic/index.ts
256
- var import_jsonc_parser = require("jsonc-parser");
253
+ // src/config/microfrontends/server/index.ts
254
+ var import_node_fs7 = __toESM(require("fs"), 1);
255
+ var import_node_path8 = require("path");
256
+
257
+ // src/bin/logger.ts
258
+ function debug(...args) {
259
+ if (process.env.MFE_DEBUG) {
260
+ console.log(...args);
261
+ }
262
+ }
263
+ function info(...args) {
264
+ console.log(...args);
265
+ }
266
+ function warn(...args) {
267
+ console.warn(...args);
268
+ }
269
+ function error(...args) {
270
+ console.error(...args);
271
+ }
272
+ var logger = {
273
+ debug,
274
+ info,
275
+ warn,
276
+ error
277
+ };
257
278
 
258
279
  // src/config/errors.ts
259
280
  var MicrofrontendError = class extends Error {
@@ -347,27 +368,18 @@ var MicrofrontendError = class extends Error {
347
368
  }
348
369
  };
349
370
 
350
- // src/config/microfrontends-config/utils/get-config-from-env.ts
351
- function getConfigStringFromEnv() {
352
- const config = process.env.MFE_CONFIG;
353
- if (!config) {
354
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
355
- type: "config",
356
- subtype: "not_found_in_env"
357
- });
358
- }
359
- return config;
360
- }
361
-
362
- // src/config/schema/utils/is-default-app.ts
363
- function isDefaultApp(a) {
364
- return !("routing" in a);
365
- }
371
+ // src/config/microfrontends-config/isomorphic/index.ts
372
+ var import_jsonc_parser = require("jsonc-parser");
366
373
 
367
374
  // src/config/overrides/constants.ts
368
375
  var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
369
376
  var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
370
377
 
378
+ // src/config/overrides/get-app-env-override-cookie-name.ts
379
+ function getAppEnvOverrideCookieName(application) {
380
+ return `${OVERRIDES_ENV_COOKIE_PREFIX}${application}`;
381
+ }
382
+
371
383
  // src/config/overrides/is-override-cookie.ts
372
384
  function isOverrideCookie(cookie) {
373
385
  return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
@@ -397,9 +409,9 @@ function parseOverrides(cookies) {
397
409
  return overridesConfig;
398
410
  }
399
411
 
400
- // src/config/overrides/get-app-env-override-cookie-name.ts
401
- function getAppEnvOverrideCookieName(application) {
402
- return `${OVERRIDES_ENV_COOKIE_PREFIX}${application}`;
412
+ // src/config/schema/utils/is-default-app.ts
413
+ function isDefaultApp(a) {
414
+ return !("routing" in a);
403
415
  }
404
416
 
405
417
  // src/config/microfrontends-config/client/index.ts
@@ -500,6 +512,183 @@ var MicrofrontendConfigClient = class {
500
512
  }
501
513
  };
502
514
 
515
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
516
+ function getConfigStringFromEnv() {
517
+ const config = process.env.MFE_CONFIG;
518
+ if (!config) {
519
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
520
+ type: "config",
521
+ subtype: "not_found_in_env"
522
+ });
523
+ }
524
+ return config;
525
+ }
526
+
527
+ // src/config/microfrontends-config/isomorphic/constants.ts
528
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
529
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
530
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
531
+
532
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
533
+ function generatePortFromName({
534
+ name,
535
+ minPort = 3e3,
536
+ maxPort = 8e3
537
+ }) {
538
+ if (!name) {
539
+ throw new Error("Name is required to generate a port");
540
+ }
541
+ let hash = 0;
542
+ for (let i = 0; i < name.length; i++) {
543
+ hash = (hash << 5) - hash + name.charCodeAt(i);
544
+ hash |= 0;
545
+ }
546
+ hash = Math.abs(hash);
547
+ const range = maxPort - minPort;
548
+ const port = minPort + hash % range;
549
+ return port;
550
+ }
551
+
552
+ // src/config/microfrontends-config/isomorphic/host.ts
553
+ var Host = class {
554
+ constructor(hostConfig, options) {
555
+ if (typeof hostConfig === "string") {
556
+ ({
557
+ protocol: this.protocol,
558
+ host: this.host,
559
+ port: this.port
560
+ } = Host.parseUrl(hostConfig));
561
+ } else {
562
+ const { protocol = "https", host, port } = hostConfig;
563
+ this.protocol = protocol;
564
+ this.host = host;
565
+ this.port = port;
566
+ }
567
+ this.local = options?.isLocal;
568
+ }
569
+ static parseUrl(url, defaultProtocol = "https") {
570
+ let hostToParse = url;
571
+ if (!/^https?:\/\//.exec(hostToParse)) {
572
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
573
+ }
574
+ const parsed = new URL(hostToParse);
575
+ if (!parsed.hostname) {
576
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
577
+ }
578
+ if (parsed.hash) {
579
+ throw new Error(
580
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
581
+ );
582
+ }
583
+ if (parsed.username || parsed.password) {
584
+ throw new Error(
585
+ Host.getMicrofrontendsError(
586
+ url,
587
+ "cannot have authentication credentials (username and/or password)"
588
+ )
589
+ );
590
+ }
591
+ if (parsed.pathname !== "/") {
592
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
593
+ }
594
+ if (parsed.search) {
595
+ throw new Error(
596
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
597
+ );
598
+ }
599
+ const protocol = parsed.protocol.slice(0, -1);
600
+ return {
601
+ protocol,
602
+ host: parsed.hostname,
603
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
604
+ };
605
+ }
606
+ static getMicrofrontendsError(url, message) {
607
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
608
+ }
609
+ isLocal() {
610
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
611
+ }
612
+ toString() {
613
+ const url = this.toUrl();
614
+ return url.toString().replace(/\/$/, "");
615
+ }
616
+ toUrl() {
617
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
618
+ return new URL(url);
619
+ }
620
+ };
621
+ var LocalHost = class extends Host {
622
+ constructor({
623
+ appName,
624
+ local
625
+ }) {
626
+ const portOverride = process.env[MFE_APP_PORT_ENV];
627
+ if (portOverride) {
628
+ const overridePort = Number.parseInt(portOverride, 10);
629
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
630
+ super({
631
+ protocol: "http",
632
+ host: "localhost",
633
+ port: overridePort
634
+ });
635
+ return;
636
+ }
637
+ }
638
+ let protocol;
639
+ let host;
640
+ let port;
641
+ if (typeof local === "number") {
642
+ port = local;
643
+ } else if (typeof local === "string") {
644
+ if (/^\d+$/.test(local)) {
645
+ port = Number.parseInt(local, 10);
646
+ } else {
647
+ const parsed = Host.parseUrl(local, "http");
648
+ protocol = parsed.protocol;
649
+ host = parsed.host;
650
+ port = parsed.port;
651
+ }
652
+ } else if (local) {
653
+ protocol = local.protocol;
654
+ host = local.host;
655
+ port = local.port;
656
+ }
657
+ super({
658
+ protocol: protocol ?? "http",
659
+ host: host ?? "localhost",
660
+ port: port ?? generatePortFromName({ name: appName })
661
+ });
662
+ }
663
+ };
664
+
665
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
666
+ var import_md5 = __toESM(require("md5"), 1);
667
+ function hashApplicationName(name) {
668
+ if (!name) {
669
+ throw new Error("Application name is required to generate hash");
670
+ }
671
+ return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
672
+ }
673
+
674
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
675
+ var PREFIX = "vc-ap";
676
+ function generateAssetPrefixFromName({
677
+ name
678
+ }) {
679
+ if (!name) {
680
+ throw new Error("Name is required to generate an asset prefix");
681
+ }
682
+ return `${PREFIX}-${hashApplicationName(name)}`;
683
+ }
684
+
685
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
686
+ function generateAutomationBypassEnvVarName({
687
+ name
688
+ }) {
689
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
690
+ }
691
+
503
692
  // src/config/microfrontends-config/isomorphic/validation.ts
504
693
  var import_path_to_regexp2 = require("path-to-regexp");
505
694
  var LIST_FORMATTER = new Intl.ListFormat("en", {
@@ -681,154 +870,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
681
870
  }
682
871
  };
683
872
 
684
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
685
- var import_md5 = __toESM(require("md5"), 1);
686
- function hashApplicationName(name) {
687
- if (!name) {
688
- throw new Error("Application name is required to generate hash");
689
- }
690
- return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
691
- }
692
-
693
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
694
- var PREFIX = "vc-ap";
695
- function generateAssetPrefixFromName({
696
- name
697
- }) {
698
- if (!name) {
699
- throw new Error("Name is required to generate an asset prefix");
700
- }
701
- return `${PREFIX}-${hashApplicationName(name)}`;
702
- }
703
-
704
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
705
- function generatePortFromName({
706
- name,
707
- minPort = 3e3,
708
- maxPort = 8e3
709
- }) {
710
- if (!name) {
711
- throw new Error("Name is required to generate a port");
712
- }
713
- let hash = 0;
714
- for (let i = 0; i < name.length; i++) {
715
- hash = (hash << 5) - hash + name.charCodeAt(i);
716
- hash |= 0;
717
- }
718
- hash = Math.abs(hash);
719
- const range = maxPort - minPort;
720
- const port = minPort + hash % range;
721
- return port;
722
- }
723
-
724
- // src/config/microfrontends-config/isomorphic/host.ts
725
- var Host = class {
726
- constructor(hostConfig, options) {
727
- if (typeof hostConfig === "string") {
728
- ({
729
- protocol: this.protocol,
730
- host: this.host,
731
- port: this.port
732
- } = Host.parseUrl(hostConfig));
733
- } else {
734
- const { protocol = "https", host, port } = hostConfig;
735
- this.protocol = protocol;
736
- this.host = host;
737
- this.port = port;
738
- }
739
- this.local = options?.isLocal;
740
- }
741
- static parseUrl(url, defaultProtocol = "https") {
742
- let hostToParse = url;
743
- if (!/^https?:\/\//.exec(hostToParse)) {
744
- hostToParse = `${defaultProtocol}://${hostToParse}`;
745
- }
746
- const parsed = new URL(hostToParse);
747
- if (!parsed.hostname) {
748
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
749
- }
750
- if (parsed.hash) {
751
- throw new Error(
752
- Host.getMicrofrontendsError(url, "cannot have a fragment")
753
- );
754
- }
755
- if (parsed.username || parsed.password) {
756
- throw new Error(
757
- Host.getMicrofrontendsError(
758
- url,
759
- "cannot have authentication credentials (username and/or password)"
760
- )
761
- );
762
- }
763
- if (parsed.pathname !== "/") {
764
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
765
- }
766
- if (parsed.search) {
767
- throw new Error(
768
- Host.getMicrofrontendsError(url, "cannot have query parameters")
769
- );
770
- }
771
- const protocol = parsed.protocol.slice(0, -1);
772
- return {
773
- protocol,
774
- host: parsed.hostname,
775
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
776
- };
777
- }
778
- static getMicrofrontendsError(url, message) {
779
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
780
- }
781
- isLocal() {
782
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
783
- }
784
- toString() {
785
- const url = this.toUrl();
786
- return url.toString().replace(/\/$/, "");
787
- }
788
- toUrl() {
789
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
790
- return new URL(url);
791
- }
792
- };
793
- var LocalHost = class extends Host {
794
- constructor({
795
- appName,
796
- local
797
- }) {
798
- let protocol;
799
- let host;
800
- let port;
801
- if (typeof local === "number") {
802
- port = local;
803
- } else if (typeof local === "string") {
804
- if (/^\d+$/.test(local)) {
805
- port = Number.parseInt(local);
806
- } else {
807
- const parsed = Host.parseUrl(local, "http");
808
- protocol = parsed.protocol;
809
- host = parsed.host;
810
- port = parsed.port;
811
- }
812
- } else if (local) {
813
- protocol = local.protocol;
814
- host = local.host;
815
- port = local.port;
816
- }
817
- super({
818
- protocol: protocol ?? "http",
819
- host: host ?? "localhost",
820
- port: port ?? generatePortFromName({ name: appName })
821
- });
822
- }
823
- };
824
-
825
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
826
- function generateAutomationBypassEnvVarName({
827
- name
828
- }) {
829
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
830
- }
831
-
832
873
  // src/config/microfrontends-config/isomorphic/application.ts
833
874
  var Application = class {
834
875
  constructor(name, {
@@ -909,9 +950,6 @@ var ChildApplication = class extends Application {
909
950
  }
910
951
  };
911
952
 
912
- // src/config/microfrontends-config/isomorphic/constants.ts
913
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
914
-
915
953
  // src/config/microfrontends-config/isomorphic/index.ts
916
954
  var MicrofrontendConfigIsomorphic = class {
917
955
  constructor({
@@ -1030,9 +1068,17 @@ var MicrofrontendConfigIsomorphic = class {
1030
1068
  return this.defaultApplication;
1031
1069
  }
1032
1070
  /**
1033
- * Returns the configured port for the local proxy
1071
+ * Returns the configured port for the local proxy.
1072
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
1034
1073
  */
1035
1074
  getLocalProxyPort() {
1075
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
1076
+ if (portOverride) {
1077
+ const port = Number.parseInt(portOverride, 10);
1078
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
1079
+ return port;
1080
+ }
1081
+ }
1036
1082
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1037
1083
  }
1038
1084
  toClientConfig(options) {
@@ -1070,23 +1116,78 @@ var MicrofrontendConfigIsomorphic = class {
1070
1116
  }
1071
1117
  };
1072
1118
 
1073
- // src/config/microfrontends/server/index.ts
1074
- var import_node_fs7 = __toESM(require("fs"), 1);
1075
- var import_node_path8 = require("path");
1119
+ // src/config/microfrontends/utils/find-config.ts
1120
+ var import_node_fs = __toESM(require("fs"), 1);
1121
+ var import_node_path = require("path");
1122
+
1123
+ // src/config/microfrontends/utils/get-config-file-name.ts
1124
+ var DEFAULT_CONFIGURATION_FILENAMES = [
1125
+ "microfrontends.json",
1126
+ "microfrontends.jsonc"
1127
+ ];
1128
+ function getPossibleConfigurationFilenames({
1129
+ customConfigFilename
1130
+ }) {
1131
+ if (customConfigFilename) {
1132
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
1133
+ throw new Error(
1134
+ `Found VC_MICROFRONTENDS_CONFIG_FILE_NAME but the name is invalid. Received: ${customConfigFilename}. The file name must end with '.json' or '.jsonc'. It's also possible for the env var to include the path, eg microfrontends-dev.json or /path/to/microfrontends-dev.json.`
1135
+ );
1136
+ }
1137
+ return Array.from(
1138
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
1139
+ );
1140
+ }
1141
+ return DEFAULT_CONFIGURATION_FILENAMES;
1142
+ }
1143
+
1144
+ // src/config/microfrontends/utils/find-config.ts
1145
+ function findConfig({
1146
+ dir,
1147
+ customConfigFilename
1148
+ }) {
1149
+ for (const filename of getPossibleConfigurationFilenames({
1150
+ customConfigFilename
1151
+ })) {
1152
+ const maybeConfig = (0, import_node_path.join)(dir, filename);
1153
+ if (import_node_fs.default.existsSync(maybeConfig)) {
1154
+ return maybeConfig;
1155
+ }
1156
+ }
1157
+ return null;
1158
+ }
1159
+
1160
+ // src/config/microfrontends/utils/find-package-root.ts
1161
+ var import_node_fs2 = __toESM(require("fs"), 1);
1162
+ var import_node_path2 = __toESM(require("path"), 1);
1163
+ var PACKAGE_JSON = "package.json";
1164
+ function findPackageRoot(startDir) {
1165
+ let currentDir = startDir || process.cwd();
1166
+ while (currentDir !== import_node_path2.default.parse(currentDir).root) {
1167
+ const pkgJsonPath = import_node_path2.default.join(currentDir, PACKAGE_JSON);
1168
+ if (import_node_fs2.default.existsSync(pkgJsonPath)) {
1169
+ return currentDir;
1170
+ }
1171
+ currentDir = import_node_path2.default.dirname(currentDir);
1172
+ }
1173
+ throw new Error(
1174
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
1175
+ );
1176
+ }
1076
1177
 
1077
1178
  // src/config/microfrontends/utils/find-repository-root.ts
1078
- var import_node_fs = __toESM(require("fs"), 1);
1079
- var import_node_path = __toESM(require("path"), 1);
1179
+ var import_node_fs3 = __toESM(require("fs"), 1);
1180
+ var import_node_path3 = __toESM(require("path"), 1);
1080
1181
  var GIT_DIRECTORY = ".git";
1081
1182
  function hasGitDirectory(dir) {
1082
- const gitPath = import_node_path.default.join(dir, GIT_DIRECTORY);
1083
- return import_node_fs.default.existsSync(gitPath) && import_node_fs.default.statSync(gitPath).isDirectory();
1183
+ const gitPath = import_node_path3.default.join(dir, GIT_DIRECTORY);
1184
+ return import_node_fs3.default.existsSync(gitPath) && import_node_fs3.default.statSync(gitPath).isDirectory();
1084
1185
  }
1085
1186
  function hasPnpmWorkspaces(dir) {
1086
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "pnpm-workspace.yaml"));
1187
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "pnpm-workspace.yaml"));
1087
1188
  }
1088
1189
  function hasPackageJson(dir) {
1089
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "package.json"));
1190
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "package.json"));
1090
1191
  }
1091
1192
  function findRepositoryRoot(startDir) {
1092
1193
  if (process.env.NX_WORKSPACE_ROOT) {
@@ -1094,14 +1195,14 @@ function findRepositoryRoot(startDir) {
1094
1195
  }
1095
1196
  let currentDir = startDir || process.cwd();
1096
1197
  let lastPackageJsonDir = null;
1097
- while (currentDir !== import_node_path.default.parse(currentDir).root) {
1198
+ while (currentDir !== import_node_path3.default.parse(currentDir).root) {
1098
1199
  if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
1099
1200
  return currentDir;
1100
1201
  }
1101
1202
  if (hasPackageJson(currentDir)) {
1102
1203
  lastPackageJsonDir = currentDir;
1103
1204
  }
1104
- currentDir = import_node_path.default.dirname(currentDir);
1205
+ currentDir = import_node_path3.default.dirname(currentDir);
1105
1206
  }
1106
1207
  if (lastPackageJsonDir) {
1107
1208
  return lastPackageJsonDir;
@@ -1111,56 +1212,88 @@ function findRepositoryRoot(startDir) {
1111
1212
  );
1112
1213
  }
1113
1214
 
1114
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
1115
- var import_node_path2 = require("path");
1116
- var import_node_fs2 = require("fs");
1117
- var import_jsonc_parser2 = require("jsonc-parser");
1118
- var import_fast_glob = __toESM(require("fast-glob"), 1);
1119
-
1120
- // src/bin/logger.ts
1121
- function debug(...args) {
1122
- if (process.env.MFE_DEBUG) {
1123
- console.log(...args);
1215
+ // src/config/microfrontends/utils/get-application-context.ts
1216
+ var import_node_fs4 = __toESM(require("fs"), 1);
1217
+ var import_node_path4 = __toESM(require("path"), 1);
1218
+ function getApplicationContext(opts) {
1219
+ if (opts?.appName) {
1220
+ logger.debug(
1221
+ "[MFE Config] Application name from appName parameter:",
1222
+ opts.appName
1223
+ );
1224
+ return { name: opts.appName };
1124
1225
  }
1125
- }
1126
- function info(...args) {
1127
- console.log(...args);
1128
- }
1129
- function warn(...args) {
1130
- console.warn(...args);
1131
- }
1132
- function error(...args) {
1133
- console.error(...args);
1134
- }
1135
- var logger = {
1136
- debug,
1137
- info,
1138
- warn,
1139
- error
1140
- };
1141
-
1142
- // src/config/microfrontends/utils/get-config-file-name.ts
1143
- var DEFAULT_CONFIGURATION_FILENAMES = [
1144
- "microfrontends.json",
1145
- "microfrontends.jsonc"
1146
- ];
1147
- function getPossibleConfigurationFilenames({
1148
- customConfigFilename
1149
- }) {
1150
- if (customConfigFilename) {
1151
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
1152
- throw new Error(
1153
- `Found VC_MICROFRONTENDS_CONFIG_FILE_NAME but the name is invalid. Received: ${customConfigFilename}. The file name must end with '.json' or '.jsonc'. It's also possible for the env var to include the path, eg microfrontends-dev.json or /path/to/microfrontends-dev.json.`
1226
+ if (process.env.VERCEL_PROJECT_NAME) {
1227
+ logger.debug(
1228
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
1229
+ process.env.VERCEL_PROJECT_NAME
1230
+ );
1231
+ return {
1232
+ name: process.env.VERCEL_PROJECT_NAME,
1233
+ projectName: process.env.VERCEL_PROJECT_NAME
1234
+ };
1235
+ }
1236
+ if (process.env.NX_TASK_TARGET_PROJECT) {
1237
+ logger.debug(
1238
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
1239
+ process.env.NX_TASK_TARGET_PROJECT
1240
+ );
1241
+ return {
1242
+ name: process.env.NX_TASK_TARGET_PROJECT,
1243
+ packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1244
+ };
1245
+ }
1246
+ try {
1247
+ const vercelProjectJsonPath = import_node_fs4.default.readFileSync(
1248
+ import_node_path4.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1249
+ "utf-8"
1250
+ );
1251
+ const projectJson = JSON.parse(vercelProjectJsonPath);
1252
+ if (projectJson.projectName) {
1253
+ logger.debug(
1254
+ "[MFE Config] Application name from .vercel/project.json:",
1255
+ projectJson.projectName
1154
1256
  );
1257
+ return {
1258
+ name: projectJson.projectName,
1259
+ projectName: projectJson.projectName
1260
+ };
1155
1261
  }
1156
- return Array.from(
1157
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
1262
+ } catch (_) {
1263
+ }
1264
+ try {
1265
+ const packageJsonString = import_node_fs4.default.readFileSync(
1266
+ import_node_path4.default.join(opts?.packageRoot || ".", "package.json"),
1267
+ "utf-8"
1268
+ );
1269
+ const packageJson = JSON.parse(packageJsonString);
1270
+ if (!packageJson.name) {
1271
+ throw new MicrofrontendError(
1272
+ `package.json file missing required field "name"`,
1273
+ {
1274
+ type: "packageJson",
1275
+ subtype: "missing_field_name",
1276
+ source: "@vercel/microfrontends/next"
1277
+ }
1278
+ );
1279
+ }
1280
+ logger.debug(
1281
+ "[MFE Config] Application name from package.json:",
1282
+ packageJson.name
1158
1283
  );
1284
+ return { name: packageJson.name, packageJsonName: packageJson.name };
1285
+ } catch (err) {
1286
+ throw MicrofrontendError.handle(err, {
1287
+ fileName: "package.json"
1288
+ });
1159
1289
  }
1160
- return DEFAULT_CONFIGURATION_FILENAMES;
1161
1290
  }
1162
1291
 
1163
1292
  // src/config/microfrontends/utils/infer-microfrontends-location.ts
1293
+ var import_node_fs5 = require("fs");
1294
+ var import_node_path5 = require("path");
1295
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
1296
+ var import_jsonc_parser2 = require("jsonc-parser");
1164
1297
  var configCache = {};
1165
1298
  function findPackageWithMicrofrontendsConfig({
1166
1299
  repositoryRoot,
@@ -1190,32 +1323,8 @@ function findPackageWithMicrofrontendsConfig({
1190
1323
  );
1191
1324
  const matchingPaths = [];
1192
1325
  for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1193
- try {
1194
- const microfrontendsJsonContent = (0, import_node_fs2.readFileSync)(
1195
- microfrontendsJsonPath,
1196
- "utf-8"
1197
- );
1198
- const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
1199
- if (microfrontendsJson.applications[applicationName]) {
1200
- logger.debug(
1201
- "[MFE Config] Found application in config:",
1202
- microfrontendsJsonPath
1203
- );
1204
- matchingPaths.push(microfrontendsJsonPath);
1205
- } else {
1206
- for (const [_, app] of Object.entries(
1207
- microfrontendsJson.applications
1208
- )) {
1209
- if (app.packageName === applicationName) {
1210
- logger.debug(
1211
- "[MFE Config] Found application via packageName in config:",
1212
- microfrontendsJsonPath
1213
- );
1214
- matchingPaths.push(microfrontendsJsonPath);
1215
- }
1216
- }
1217
- }
1218
- } catch (error2) {
1326
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1327
+ matchingPaths.push(microfrontendsJsonPath);
1219
1328
  }
1220
1329
  }
1221
1330
  logger.debug(
@@ -1230,6 +1339,34 @@ ${matchingPaths.join("\n \u2022 ")}`,
1230
1339
  );
1231
1340
  }
1232
1341
  if (matchingPaths.length === 0) {
1342
+ if (repositoryRoot && doesMisplacedConfigExist(
1343
+ repositoryRoot,
1344
+ applicationName,
1345
+ customConfigFilename
1346
+ )) {
1347
+ logger.debug(
1348
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1349
+ );
1350
+ const misplacedConfigPath = (0, import_node_path5.join)(
1351
+ repositoryRoot,
1352
+ ".vercel",
1353
+ customConfigFilename || "microfrontends.json"
1354
+ );
1355
+ throw new MicrofrontendError(
1356
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1357
+
1358
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1359
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1360
+
1361
+ To fix this:
1362
+ 1. If using \`vercel link\`, run it with \`vercel link --repo\` to handle monorepos, or run \`vercel microfrontends pull --cwd=<application-directory>\` to make sure it pulls the \`microfrontends.json\` file to the correct location
1363
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1364
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1365
+
1366
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1367
+ { type: "config", subtype: "inference_failed" }
1368
+ );
1369
+ }
1233
1370
  let additionalErrorMessage = "";
1234
1371
  if (microfrontendsJsonPaths.length > 0) {
1235
1372
  if (!applicationContext.projectName) {
@@ -1254,7 +1391,7 @@ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1254
1391
  );
1255
1392
  }
1256
1393
  const [packageJsonPath] = matchingPaths;
1257
- return (0, import_node_path2.dirname)(packageJsonPath);
1394
+ return (0, import_node_path5.dirname)(packageJsonPath);
1258
1395
  } catch (error2) {
1259
1396
  if (error2 instanceof MicrofrontendError) {
1260
1397
  throw error2;
@@ -1277,29 +1414,76 @@ function inferMicrofrontendsLocation(opts) {
1277
1414
  configCache[cacheKey] = result;
1278
1415
  return result;
1279
1416
  }
1417
+ function existsSync(path7) {
1418
+ try {
1419
+ (0, import_node_fs5.statSync)(path7);
1420
+ return true;
1421
+ } catch (_) {
1422
+ return false;
1423
+ }
1424
+ }
1425
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1426
+ logger.debug(
1427
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1428
+ );
1429
+ const misplacedConfigPath = (0, import_node_path5.join)(
1430
+ repositoryRoot,
1431
+ ".vercel",
1432
+ customConfigFilename || "microfrontends.json"
1433
+ );
1434
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1435
+ }
1436
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1437
+ try {
1438
+ const microfrontendsJsonContent = (0, import_node_fs5.readFileSync)(
1439
+ microfrontendsJsonPath,
1440
+ "utf-8"
1441
+ );
1442
+ const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
1443
+ if (microfrontendsJson.applications[applicationName]) {
1444
+ logger.debug(
1445
+ "[MFE Config] Found application in config:",
1446
+ microfrontendsJsonPath
1447
+ );
1448
+ return true;
1449
+ }
1450
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1451
+ if (app.packageName === applicationName) {
1452
+ logger.debug(
1453
+ "[MFE Config] Found application via packageName in config:",
1454
+ microfrontendsJsonPath
1455
+ );
1456
+ return true;
1457
+ }
1458
+ }
1459
+ } catch (error2) {
1460
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1461
+ }
1462
+ return false;
1463
+ }
1280
1464
 
1281
1465
  // src/config/microfrontends/utils/is-monorepo.ts
1282
- var import_node_fs3 = __toESM(require("fs"), 1);
1283
- var import_node_path3 = __toESM(require("path"), 1);
1466
+ var import_node_fs6 = __toESM(require("fs"), 1);
1467
+ var import_node_path6 = __toESM(require("path"), 1);
1284
1468
  function isMonorepo({
1285
1469
  repositoryRoot
1286
1470
  }) {
1287
1471
  try {
1288
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1472
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1289
1473
  return true;
1290
1474
  }
1291
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "vlt-workspaces.json"))) {
1475
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "vlt-workspaces.json"))) {
1292
1476
  return true;
1293
1477
  }
1294
- if (process.env.NX_WORKSPACE_ROOT === import_node_path3.default.resolve(repositoryRoot)) {
1478
+ if (process.env.NX_WORKSPACE_ROOT === import_node_path6.default.resolve(repositoryRoot)) {
1295
1479
  return true;
1296
1480
  }
1297
- const packageJsonPath = import_node_path3.default.join(repositoryRoot, "package.json");
1298
- if (!import_node_fs3.default.existsSync(packageJsonPath)) {
1481
+ const packageJsonPath = import_node_path6.default.join(repositoryRoot, "package.json");
1482
+ if (!import_node_fs6.default.existsSync(packageJsonPath)) {
1299
1483
  return false;
1300
1484
  }
1301
1485
  const packageJson = JSON.parse(
1302
- import_node_fs3.default.readFileSync(packageJsonPath, "utf-8")
1486
+ import_node_fs6.default.readFileSync(packageJsonPath, "utf-8")
1303
1487
  );
1304
1488
  return packageJson.workspaces !== void 0;
1305
1489
  } catch (error2) {
@@ -1308,104 +1492,6 @@ function isMonorepo({
1308
1492
  }
1309
1493
  }
1310
1494
 
1311
- // src/config/microfrontends/utils/find-package-root.ts
1312
- var import_node_fs4 = __toESM(require("fs"), 1);
1313
- var import_node_path4 = __toESM(require("path"), 1);
1314
- var PACKAGE_JSON = "package.json";
1315
- function findPackageRoot(startDir) {
1316
- let currentDir = startDir || process.cwd();
1317
- while (currentDir !== import_node_path4.default.parse(currentDir).root) {
1318
- const pkgJsonPath = import_node_path4.default.join(currentDir, PACKAGE_JSON);
1319
- if (import_node_fs4.default.existsSync(pkgJsonPath)) {
1320
- return currentDir;
1321
- }
1322
- currentDir = import_node_path4.default.dirname(currentDir);
1323
- }
1324
- throw new Error(
1325
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
1326
- );
1327
- }
1328
-
1329
- // src/config/microfrontends/utils/find-config.ts
1330
- var import_node_fs5 = __toESM(require("fs"), 1);
1331
- var import_node_path5 = require("path");
1332
- function findConfig({
1333
- dir,
1334
- customConfigFilename
1335
- }) {
1336
- for (const filename of getPossibleConfigurationFilenames({
1337
- customConfigFilename
1338
- })) {
1339
- const maybeConfig = (0, import_node_path5.join)(dir, filename);
1340
- if (import_node_fs5.default.existsSync(maybeConfig)) {
1341
- return maybeConfig;
1342
- }
1343
- }
1344
- return null;
1345
- }
1346
-
1347
- // src/config/microfrontends/utils/get-application-context.ts
1348
- var import_node_fs6 = __toESM(require("fs"), 1);
1349
- var import_node_path6 = __toESM(require("path"), 1);
1350
- function getApplicationContext(opts) {
1351
- if (opts?.appName) {
1352
- logger.debug("[MFE Config] Application name from appName parameter:", opts.appName);
1353
- return { name: opts.appName };
1354
- }
1355
- if (process.env.VERCEL_PROJECT_NAME) {
1356
- logger.debug("[MFE Config] Application name from VERCEL_PROJECT_NAME:", process.env.VERCEL_PROJECT_NAME);
1357
- return {
1358
- name: process.env.VERCEL_PROJECT_NAME,
1359
- projectName: process.env.VERCEL_PROJECT_NAME
1360
- };
1361
- }
1362
- if (process.env.NX_TASK_TARGET_PROJECT) {
1363
- logger.debug("[MFE Config] Application name from NX_TASK_TARGET_PROJECT:", process.env.NX_TASK_TARGET_PROJECT);
1364
- return {
1365
- name: process.env.NX_TASK_TARGET_PROJECT,
1366
- packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1367
- };
1368
- }
1369
- try {
1370
- const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1371
- import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1372
- "utf-8"
1373
- );
1374
- const projectJson = JSON.parse(vercelProjectJsonPath);
1375
- if (projectJson.projectName) {
1376
- logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
1377
- return {
1378
- name: projectJson.projectName,
1379
- projectName: projectJson.projectName
1380
- };
1381
- }
1382
- } catch (_) {
1383
- }
1384
- try {
1385
- const packageJsonString = import_node_fs6.default.readFileSync(
1386
- import_node_path6.default.join(opts?.packageRoot || ".", "package.json"),
1387
- "utf-8"
1388
- );
1389
- const packageJson = JSON.parse(packageJsonString);
1390
- if (!packageJson.name) {
1391
- throw new MicrofrontendError(
1392
- `package.json file missing required field "name"`,
1393
- {
1394
- type: "packageJson",
1395
- subtype: "missing_field_name",
1396
- source: "@vercel/microfrontends/next"
1397
- }
1398
- );
1399
- }
1400
- logger.debug("[MFE Config] Application name from package.json:", packageJson.name);
1401
- return { name: packageJson.name, packageJsonName: packageJson.name };
1402
- } catch (err) {
1403
- throw MicrofrontendError.handle(err, {
1404
- fileName: "package.json"
1405
- });
1406
- }
1407
- }
1408
-
1409
1495
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1410
1496
  var import_node_path7 = __toESM(require("path"), 1);
1411
1497
 
@@ -1419,8 +1505,8 @@ function getOutputFilePath() {
1419
1505
  }
1420
1506
 
1421
1507
  // src/config/microfrontends/server/validation.ts
1422
- var import_jsonc_parser3 = require("jsonc-parser");
1423
1508
  var import_ajv = require("ajv");
1509
+ var import_jsonc_parser3 = require("jsonc-parser");
1424
1510
 
1425
1511
  // schema/schema.json
1426
1512
  var schema_default = {
@@ -1448,9 +1534,7 @@ var schema_default = {
1448
1534
  description: "Optional configuration options for the microfrontend."
1449
1535
  }
1450
1536
  },
1451
- required: [
1452
- "applications"
1453
- ],
1537
+ required: ["applications"],
1454
1538
  additionalProperties: false,
1455
1539
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1456
1540
  },
@@ -1487,19 +1571,14 @@ var schema_default = {
1487
1571
  description: "Development configuration for the default application."
1488
1572
  }
1489
1573
  },
1490
- required: [
1491
- "development"
1492
- ],
1574
+ required: ["development"],
1493
1575
  additionalProperties: false
1494
1576
  },
1495
1577
  DefaultDevelopment: {
1496
1578
  type: "object",
1497
1579
  properties: {
1498
1580
  local: {
1499
- type: [
1500
- "number",
1501
- "string"
1502
- ],
1581
+ type: ["number", "string"],
1503
1582
  description: "A local port number or host that this application runs on when it is running locally. If passing a string, include the protocol (optional), host (required) and port (optional).\n\nExamples of valid values: 8080, my.localhost.me, my.localhost.me:8080, https://my.localhost.me, https://my.localhost.me:8080.\n\nThe default value is http://localhost:<port> where port is a stable, unique port number (based on the application name).\n\nSee https://vercel.com/docs/microfrontends/local-development."
1504
1583
  },
1505
1584
  task: {
@@ -1511,9 +1590,7 @@ var schema_default = {
1511
1590
  description: "Fallback for local development, could point to any environment. This is required for the default app. This value is used as the fallback for child apps as well if they do not have a fallback.\n\nIf passing a string, include the protocol (optional), host (required) and port (optional). For example: `https://this.ismyhost:8080`. If omitted, the protocol defaults to HTTPS. If omitted, the port defaults to `80` for HTTP and `443` for HTTPS.\n\nSee https://vercel.com/docs/microfrontends/local-development."
1512
1591
  }
1513
1592
  },
1514
- required: [
1515
- "fallback"
1516
- ],
1593
+ required: ["fallback"],
1517
1594
  additionalProperties: false
1518
1595
  },
1519
1596
  ChildApplication: {
@@ -1536,19 +1613,14 @@ var schema_default = {
1536
1613
  description: "The name of the asset prefix to use instead of the auto-generated name.\n\nThe asset prefix is used to prefix all paths to static assets, such as JS, CSS, or images that are served by a specific application. It is necessary to ensure there are no conflicts with other applications on the same domain.\n\nAn auto-generated asset prefix of the form `vc-ap-<hash>` is used when this field is not provided.\n\nWhen this field is provided, `/${assetPrefix}/:path*` must also be added to the list of paths in the `routing` field. Changing the asset prefix after a microfrontend application has already been deployed is not a forwards and backwards compatible change, and the asset prefix should be added to the `routing` field and deployed before setting the `assetPrefix` field.\n\nThe default value is the auto-generated asset prefix of the form `vc-ap-<hash>`.\n\nSee https://vercel.com/docs/microfrontends/path-routing#asset-prefix."
1537
1614
  }
1538
1615
  },
1539
- required: [
1540
- "routing"
1541
- ],
1616
+ required: ["routing"],
1542
1617
  additionalProperties: false
1543
1618
  },
1544
1619
  ChildDevelopment: {
1545
1620
  type: "object",
1546
1621
  properties: {
1547
1622
  local: {
1548
- type: [
1549
- "number",
1550
- "string"
1551
- ],
1623
+ type: ["number", "string"],
1552
1624
  description: "A local port number or host that this application runs on when it is running locally. If passing a string, include the protocol (optional), host (required) and port (optional).\n\nExamples of valid values: 8080, my.localhost.me, my.localhost.me:8080, https://my.localhost.me, https://my.localhost.me:8080.\n\nThe default value is http://localhost:<port> where port is a stable, unique port number (based on the application name).\n\nSee https://vercel.com/docs/microfrontends/local-development."
1553
1625
  },
1554
1626
  task: {
@@ -1588,9 +1660,7 @@ var schema_default = {
1588
1660
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1589
1661
  }
1590
1662
  },
1591
- required: [
1592
- "paths"
1593
- ],
1663
+ required: ["paths"],
1594
1664
  additionalProperties: false,
1595
1665
  description: "A group of paths that is routed to this application."
1596
1666
  },
@@ -2663,6 +2733,11 @@ var LocalProxy = class {
2663
2733
  `The following apps passed via --local-apps are not in the microfrontends config: ${unknownApps.join(", ")} (microfrontends config contains: ${Array.from(allApps).join(", ")})`
2664
2734
  );
2665
2735
  }
2736
+ if (process.env[MFE_APP_PORT_ENV] && localApps.length > 1) {
2737
+ throw new Error(
2738
+ `${MFE_APP_PORT_ENV} cannot be used when multiple applications are running locally. You have ${localApps.length} local apps: ${localApps.join(", ")}. Either run a single app locally or remove the ${MFE_APP_PORT_ENV} environment variable.`
2739
+ );
2740
+ }
2666
2741
  }
2667
2742
  startServer() {
2668
2743
  const httpServer = http.createServer(
@@ -2934,10 +3009,23 @@ function formatProxyTarget(target) {
2934
3009
  var import_node_process = require("process");
2935
3010
 
2936
3011
  // src/utils/mfe-port.ts
2937
- var import_node_path9 = __toESM(require("path"), 1);
2938
3012
  var import_node_fs8 = __toESM(require("fs"), 1);
3013
+ var import_node_path9 = __toESM(require("path"), 1);
3014
+ var MFE_APP_PORT_ENV2 = "MFE_APP_PORT";
2939
3015
  function mfePort(packageDir) {
2940
3016
  const { name: appName, version } = getPackageJson(packageDir);
3017
+ const portOverride = process.env[MFE_APP_PORT_ENV2];
3018
+ if (portOverride) {
3019
+ const port = Number.parseInt(portOverride, 10);
3020
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
3021
+ return {
3022
+ name: appName,
3023
+ version,
3024
+ port,
3025
+ overridden: true
3026
+ };
3027
+ }
3028
+ }
2941
3029
  try {
2942
3030
  const result = loadConfig({ packageDir, appName });
2943
3031
  const { port } = result;
@@ -2975,10 +3063,11 @@ function displayPort() {
2975
3063
  header(portInfo);
2976
3064
  logger.info(portInfo.port);
2977
3065
  }
2978
- function header({ name, version, port }) {
3066
+ function header({ name, version, port, overridden }) {
3067
+ const portSource = overridden ? `${port} (override via ${MFE_APP_PORT_ENV2})` : port;
2979
3068
  logger.error(`
2980
3069
  \u25B2 ${name}@${version}
2981
- \xB7 setting port to ${port}
3070
+ \xB7 setting port to ${portSource}
2982
3071
  `);
2983
3072
  }
2984
3073