@vercel/microfrontends 2.2.0 → 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 +25 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +551 -381
  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 +643 -489
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +645 -491
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +671 -519
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +669 -517
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +661 -509
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +663 -511
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +117 -23
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +4 -4
  25. package/dist/microfrontends/utils.js +118 -24
  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 +784 -626
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +782 -624
  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 +689 -522
  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 +687 -521
  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.0",
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,34 +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/config/microfrontends/utils/get-config-file-name.ts
1121
- var DEFAULT_CONFIGURATION_FILENAMES = [
1122
- "microfrontends.json",
1123
- "microfrontends.jsonc"
1124
- ];
1125
- function getPossibleConfigurationFilenames({
1126
- customConfigFilename
1127
- }) {
1128
- if (customConfigFilename) {
1129
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
1130
- throw new Error(
1131
- `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.`
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 };
1225
+ }
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
1132
1256
  );
1257
+ return {
1258
+ name: projectJson.projectName,
1259
+ projectName: projectJson.projectName
1260
+ };
1133
1261
  }
1134
- return Array.from(
1135
- /* @__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
1136
1283
  );
1284
+ return { name: packageJson.name, packageJsonName: packageJson.name };
1285
+ } catch (err) {
1286
+ throw MicrofrontendError.handle(err, {
1287
+ fileName: "package.json"
1288
+ });
1137
1289
  }
1138
- return DEFAULT_CONFIGURATION_FILENAMES;
1139
1290
  }
1140
1291
 
1141
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");
1142
1297
  var configCache = {};
1143
1298
  function findPackageWithMicrofrontendsConfig({
1144
1299
  repositoryRoot,
@@ -1146,6 +1301,10 @@ function findPackageWithMicrofrontendsConfig({
1146
1301
  customConfigFilename
1147
1302
  }) {
1148
1303
  const applicationName = applicationContext.name;
1304
+ logger.debug(
1305
+ "[MFE Config] Searching repository for configs containing application:",
1306
+ applicationName
1307
+ );
1149
1308
  try {
1150
1309
  const microfrontendsJsonPaths = import_fast_glob.default.globSync(
1151
1310
  `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
@@ -1157,28 +1316,21 @@ function findPackageWithMicrofrontendsConfig({
1157
1316
  ignore: ["**/node_modules/**", "**/.git/**"]
1158
1317
  }
1159
1318
  );
1319
+ logger.debug(
1320
+ "[MFE Config] Found",
1321
+ microfrontendsJsonPaths.length,
1322
+ "config file(s) in repository"
1323
+ );
1160
1324
  const matchingPaths = [];
1161
1325
  for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1162
- try {
1163
- const microfrontendsJsonContent = (0, import_node_fs2.readFileSync)(
1164
- microfrontendsJsonPath,
1165
- "utf-8"
1166
- );
1167
- const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
1168
- if (microfrontendsJson.applications[applicationName]) {
1169
- matchingPaths.push(microfrontendsJsonPath);
1170
- } else {
1171
- for (const [_, app] of Object.entries(
1172
- microfrontendsJson.applications
1173
- )) {
1174
- if (app.packageName === applicationName) {
1175
- matchingPaths.push(microfrontendsJsonPath);
1176
- }
1177
- }
1178
- }
1179
- } catch (error2) {
1326
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1327
+ matchingPaths.push(microfrontendsJsonPath);
1180
1328
  }
1181
1329
  }
1330
+ logger.debug(
1331
+ "[MFE Config] Total matching config files:",
1332
+ matchingPaths.length
1333
+ );
1182
1334
  if (matchingPaths.length > 1) {
1183
1335
  throw new MicrofrontendError(
1184
1336
  `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
@@ -1187,6 +1339,34 @@ ${matchingPaths.join("\n \u2022 ")}`,
1187
1339
  );
1188
1340
  }
1189
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
+ }
1190
1370
  let additionalErrorMessage = "";
1191
1371
  if (microfrontendsJsonPaths.length > 0) {
1192
1372
  if (!applicationContext.projectName) {
@@ -1211,7 +1391,7 @@ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1211
1391
  );
1212
1392
  }
1213
1393
  const [packageJsonPath] = matchingPaths;
1214
- return (0, import_node_path2.dirname)(packageJsonPath);
1394
+ return (0, import_node_path5.dirname)(packageJsonPath);
1215
1395
  } catch (error2) {
1216
1396
  if (error2 instanceof MicrofrontendError) {
1217
1397
  throw error2;
@@ -1234,53 +1414,76 @@ function inferMicrofrontendsLocation(opts) {
1234
1414
  configCache[cacheKey] = result;
1235
1415
  return result;
1236
1416
  }
1237
-
1238
- // src/config/microfrontends/utils/is-monorepo.ts
1239
- var import_node_fs3 = __toESM(require("fs"), 1);
1240
- var import_node_path3 = __toESM(require("path"), 1);
1241
-
1242
- // src/bin/logger.ts
1243
- function debug(...args) {
1244
- if (process.env.MFE_DEBUG) {
1245
- console.log(...args);
1417
+ function existsSync(path7) {
1418
+ try {
1419
+ (0, import_node_fs5.statSync)(path7);
1420
+ return true;
1421
+ } catch (_) {
1422
+ return false;
1246
1423
  }
1247
1424
  }
1248
- function info(...args) {
1249
- console.log(...args);
1250
- }
1251
- function warn(...args) {
1252
- console.warn(...args);
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);
1253
1435
  }
1254
- function error(...args) {
1255
- console.error(...args);
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;
1256
1463
  }
1257
- var logger = {
1258
- debug,
1259
- info,
1260
- warn,
1261
- error
1262
- };
1263
1464
 
1264
1465
  // src/config/microfrontends/utils/is-monorepo.ts
1466
+ var import_node_fs6 = __toESM(require("fs"), 1);
1467
+ var import_node_path6 = __toESM(require("path"), 1);
1265
1468
  function isMonorepo({
1266
1469
  repositoryRoot
1267
1470
  }) {
1268
1471
  try {
1269
- 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"))) {
1270
1473
  return true;
1271
1474
  }
1272
- 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"))) {
1273
1476
  return true;
1274
1477
  }
1275
- 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)) {
1276
1479
  return true;
1277
1480
  }
1278
- const packageJsonPath = import_node_path3.default.join(repositoryRoot, "package.json");
1279
- 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)) {
1280
1483
  return false;
1281
1484
  }
1282
1485
  const packageJson = JSON.parse(
1283
- import_node_fs3.default.readFileSync(packageJsonPath, "utf-8")
1486
+ import_node_fs6.default.readFileSync(packageJsonPath, "utf-8")
1284
1487
  );
1285
1488
  return packageJson.workspaces !== void 0;
1286
1489
  } catch (error2) {
@@ -1289,99 +1492,6 @@ function isMonorepo({
1289
1492
  }
1290
1493
  }
1291
1494
 
1292
- // src/config/microfrontends/utils/find-package-root.ts
1293
- var import_node_fs4 = __toESM(require("fs"), 1);
1294
- var import_node_path4 = __toESM(require("path"), 1);
1295
- var PACKAGE_JSON = "package.json";
1296
- function findPackageRoot(startDir) {
1297
- let currentDir = startDir || process.cwd();
1298
- while (currentDir !== import_node_path4.default.parse(currentDir).root) {
1299
- const pkgJsonPath = import_node_path4.default.join(currentDir, PACKAGE_JSON);
1300
- if (import_node_fs4.default.existsSync(pkgJsonPath)) {
1301
- return currentDir;
1302
- }
1303
- currentDir = import_node_path4.default.dirname(currentDir);
1304
- }
1305
- throw new Error(
1306
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
1307
- );
1308
- }
1309
-
1310
- // src/config/microfrontends/utils/find-config.ts
1311
- var import_node_fs5 = __toESM(require("fs"), 1);
1312
- var import_node_path5 = require("path");
1313
- function findConfig({
1314
- dir,
1315
- customConfigFilename
1316
- }) {
1317
- for (const filename of getPossibleConfigurationFilenames({
1318
- customConfigFilename
1319
- })) {
1320
- const maybeConfig = (0, import_node_path5.join)(dir, filename);
1321
- if (import_node_fs5.default.existsSync(maybeConfig)) {
1322
- return maybeConfig;
1323
- }
1324
- }
1325
- return null;
1326
- }
1327
-
1328
- // src/config/microfrontends/utils/get-application-context.ts
1329
- var import_node_fs6 = __toESM(require("fs"), 1);
1330
- var import_node_path6 = __toESM(require("path"), 1);
1331
- function getApplicationContext(opts) {
1332
- if (opts?.appName) {
1333
- return { name: opts.appName };
1334
- }
1335
- if (process.env.VERCEL_PROJECT_NAME) {
1336
- return {
1337
- name: process.env.VERCEL_PROJECT_NAME,
1338
- projectName: process.env.VERCEL_PROJECT_NAME
1339
- };
1340
- }
1341
- if (process.env.NX_TASK_TARGET_PROJECT) {
1342
- return {
1343
- name: process.env.NX_TASK_TARGET_PROJECT,
1344
- packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1345
- };
1346
- }
1347
- try {
1348
- const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1349
- import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1350
- "utf-8"
1351
- );
1352
- const projectJson = JSON.parse(vercelProjectJsonPath);
1353
- if (projectJson.projectName) {
1354
- return {
1355
- name: projectJson.projectName,
1356
- projectName: projectJson.projectName
1357
- };
1358
- }
1359
- } catch (_) {
1360
- }
1361
- try {
1362
- const packageJsonString = import_node_fs6.default.readFileSync(
1363
- import_node_path6.default.join(opts?.packageRoot || ".", "package.json"),
1364
- "utf-8"
1365
- );
1366
- const packageJson = JSON.parse(packageJsonString);
1367
- if (!packageJson.name) {
1368
- throw new MicrofrontendError(
1369
- `package.json file missing required field "name"`,
1370
- {
1371
- type: "packageJson",
1372
- subtype: "missing_field_name",
1373
- source: "@vercel/microfrontends/next"
1374
- }
1375
- );
1376
- }
1377
- return { name: packageJson.name, packageJsonName: packageJson.name };
1378
- } catch (err) {
1379
- throw MicrofrontendError.handle(err, {
1380
- fileName: "package.json"
1381
- });
1382
- }
1383
- }
1384
-
1385
1495
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1386
1496
  var import_node_path7 = __toESM(require("path"), 1);
1387
1497
 
@@ -1395,8 +1505,8 @@ function getOutputFilePath() {
1395
1505
  }
1396
1506
 
1397
1507
  // src/config/microfrontends/server/validation.ts
1398
- var import_jsonc_parser3 = require("jsonc-parser");
1399
1508
  var import_ajv = require("ajv");
1509
+ var import_jsonc_parser3 = require("jsonc-parser");
1400
1510
 
1401
1511
  // schema/schema.json
1402
1512
  var schema_default = {
@@ -1424,9 +1534,7 @@ var schema_default = {
1424
1534
  description: "Optional configuration options for the microfrontend."
1425
1535
  }
1426
1536
  },
1427
- required: [
1428
- "applications"
1429
- ],
1537
+ required: ["applications"],
1430
1538
  additionalProperties: false,
1431
1539
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1432
1540
  },
@@ -1463,19 +1571,14 @@ var schema_default = {
1463
1571
  description: "Development configuration for the default application."
1464
1572
  }
1465
1573
  },
1466
- required: [
1467
- "development"
1468
- ],
1574
+ required: ["development"],
1469
1575
  additionalProperties: false
1470
1576
  },
1471
1577
  DefaultDevelopment: {
1472
1578
  type: "object",
1473
1579
  properties: {
1474
1580
  local: {
1475
- type: [
1476
- "number",
1477
- "string"
1478
- ],
1581
+ type: ["number", "string"],
1479
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."
1480
1583
  },
1481
1584
  task: {
@@ -1487,9 +1590,7 @@ var schema_default = {
1487
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."
1488
1591
  }
1489
1592
  },
1490
- required: [
1491
- "fallback"
1492
- ],
1593
+ required: ["fallback"],
1493
1594
  additionalProperties: false
1494
1595
  },
1495
1596
  ChildApplication: {
@@ -1512,19 +1613,14 @@ var schema_default = {
1512
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."
1513
1614
  }
1514
1615
  },
1515
- required: [
1516
- "routing"
1517
- ],
1616
+ required: ["routing"],
1518
1617
  additionalProperties: false
1519
1618
  },
1520
1619
  ChildDevelopment: {
1521
1620
  type: "object",
1522
1621
  properties: {
1523
1622
  local: {
1524
- type: [
1525
- "number",
1526
- "string"
1527
- ],
1623
+ type: ["number", "string"],
1528
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."
1529
1625
  },
1530
1626
  task: {
@@ -1564,9 +1660,7 @@ var schema_default = {
1564
1660
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1565
1661
  }
1566
1662
  },
1567
- required: [
1568
- "paths"
1569
- ],
1663
+ required: ["paths"],
1570
1664
  additionalProperties: false,
1571
1665
  description: "A group of paths that is routed to this application."
1572
1666
  },
@@ -1745,7 +1839,13 @@ var MicrofrontendsServer = class {
1745
1839
  filePath,
1746
1840
  cookies
1747
1841
  } = {}) {
1842
+ logger.debug("[MFE Config] Starting config inference", {
1843
+ appName,
1844
+ directory: directory || process.cwd(),
1845
+ filePath
1846
+ });
1748
1847
  if (filePath) {
1848
+ logger.debug("[MFE Config] Using explicit filePath:", filePath);
1749
1849
  return MicrofrontendsServer.fromFile({
1750
1850
  filePath,
1751
1851
  cookies
@@ -1753,16 +1853,25 @@ var MicrofrontendsServer = class {
1753
1853
  }
1754
1854
  try {
1755
1855
  const packageRoot = findPackageRoot(directory);
1856
+ logger.debug("[MFE Config] Package root:", packageRoot);
1756
1857
  const applicationContext = getApplicationContext({
1757
1858
  appName,
1758
1859
  packageRoot
1759
1860
  });
1861
+ logger.debug("[MFE Config] Application context:", applicationContext);
1760
1862
  const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
1863
+ if (customConfigFilename) {
1864
+ logger.debug(
1865
+ "[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
1866
+ customConfigFilename
1867
+ );
1868
+ }
1761
1869
  const maybeConfig = findConfig({
1762
1870
  dir: packageRoot,
1763
1871
  customConfigFilename
1764
1872
  });
1765
1873
  if (maybeConfig) {
1874
+ logger.debug("[MFE Config] Config found at package root:", maybeConfig);
1766
1875
  return MicrofrontendsServer.fromFile({
1767
1876
  filePath: maybeConfig,
1768
1877
  cookies
@@ -1770,42 +1879,78 @@ var MicrofrontendsServer = class {
1770
1879
  }
1771
1880
  const repositoryRoot = findRepositoryRoot();
1772
1881
  const isMonorepo2 = isMonorepo({ repositoryRoot });
1882
+ logger.debug(
1883
+ "[MFE Config] Repository root:",
1884
+ repositoryRoot,
1885
+ "Is monorepo:",
1886
+ isMonorepo2
1887
+ );
1773
1888
  const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
1774
1889
  if (typeof configFromEnv === "string") {
1890
+ logger.debug(
1891
+ "[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
1892
+ configFromEnv
1893
+ );
1775
1894
  const maybeConfigFromEnv = (0, import_node_path8.resolve)(packageRoot, configFromEnv);
1776
1895
  if (maybeConfigFromEnv) {
1896
+ logger.debug(
1897
+ "[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
1898
+ maybeConfigFromEnv
1899
+ );
1777
1900
  return MicrofrontendsServer.fromFile({
1778
1901
  filePath: maybeConfigFromEnv,
1779
1902
  cookies
1780
1903
  });
1781
1904
  }
1782
1905
  } else {
1906
+ const vercelDir = (0, import_node_path8.join)(packageRoot, ".vercel");
1907
+ logger.debug(
1908
+ "[MFE Config] Searching for config in .vercel directory:",
1909
+ vercelDir
1910
+ );
1783
1911
  const maybeConfigFromVercel = findConfig({
1784
- dir: (0, import_node_path8.join)(packageRoot, ".vercel"),
1912
+ dir: vercelDir,
1785
1913
  customConfigFilename
1786
1914
  });
1787
1915
  if (maybeConfigFromVercel) {
1916
+ logger.debug(
1917
+ "[MFE Config] Config found in .vercel directory:",
1918
+ maybeConfigFromVercel
1919
+ );
1788
1920
  return MicrofrontendsServer.fromFile({
1789
1921
  filePath: maybeConfigFromVercel,
1790
1922
  cookies
1791
1923
  });
1792
1924
  }
1793
1925
  if (isMonorepo2) {
1926
+ logger.debug(
1927
+ "[MFE Config] Inferring microfrontends location in monorepo for application:",
1928
+ applicationContext.name
1929
+ );
1794
1930
  const defaultPackage = inferMicrofrontendsLocation({
1795
1931
  repositoryRoot,
1796
1932
  applicationContext,
1797
1933
  customConfigFilename
1798
1934
  });
1935
+ logger.debug(
1936
+ "[MFE Config] Inferred package location:",
1937
+ defaultPackage
1938
+ );
1799
1939
  const maybeConfigFromDefault = findConfig({
1800
1940
  dir: defaultPackage,
1801
1941
  customConfigFilename
1802
1942
  });
1803
1943
  if (maybeConfigFromDefault) {
1944
+ logger.debug(
1945
+ "[MFE Config] Config found in inferred package:",
1946
+ maybeConfigFromDefault
1947
+ );
1804
1948
  return MicrofrontendsServer.fromFile({
1805
1949
  filePath: maybeConfigFromDefault,
1806
1950
  cookies
1807
1951
  });
1808
1952
  }
1953
+ logger.debug("[MFE Config] No config found in inferred package");
1809
1954
  }
1810
1955
  }
1811
1956
  throw new MicrofrontendError(
@@ -1831,8 +1976,13 @@ var MicrofrontendsServer = class {
1831
1976
  cookies
1832
1977
  }) {
1833
1978
  try {
1979
+ logger.debug("[MFE Config] Reading config from file:", filePath);
1834
1980
  const configJson = import_node_fs7.default.readFileSync(filePath, "utf-8");
1835
1981
  const config = MicrofrontendsServer.validate(configJson);
1982
+ logger.debug(
1983
+ "[MFE Config] Config loaded with applications:",
1984
+ Object.keys(config.applications)
1985
+ );
1836
1986
  return new MicrofrontendsServer({
1837
1987
  config,
1838
1988
  overrides: cookies ? parseOverrides(cookies) : void 0
@@ -2583,6 +2733,11 @@ var LocalProxy = class {
2583
2733
  `The following apps passed via --local-apps are not in the microfrontends config: ${unknownApps.join(", ")} (microfrontends config contains: ${Array.from(allApps).join(", ")})`
2584
2734
  );
2585
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
+ }
2586
2741
  }
2587
2742
  startServer() {
2588
2743
  const httpServer = http.createServer(
@@ -2854,10 +3009,23 @@ function formatProxyTarget(target) {
2854
3009
  var import_node_process = require("process");
2855
3010
 
2856
3011
  // src/utils/mfe-port.ts
2857
- var import_node_path9 = __toESM(require("path"), 1);
2858
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";
2859
3015
  function mfePort(packageDir) {
2860
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
+ }
2861
3029
  try {
2862
3030
  const result = loadConfig({ packageDir, appName });
2863
3031
  const { port } = result;
@@ -2890,14 +3058,16 @@ function loadConfig({
2890
3058
 
2891
3059
  // src/bin/port.ts
2892
3060
  function displayPort() {
3061
+ delete process.env.MFE_DEBUG;
2893
3062
  const portInfo = mfePort((0, import_node_process.cwd)());
2894
3063
  header(portInfo);
2895
3064
  logger.info(portInfo.port);
2896
3065
  }
2897
- function header({ name, version, port }) {
3066
+ function header({ name, version, port, overridden }) {
3067
+ const portSource = overridden ? `${port} (override via ${MFE_APP_PORT_ENV2})` : port;
2898
3068
  logger.error(`
2899
3069
  \u25B2 ${name}@${version}
2900
- \xB7 setting port to ${port}
3070
+ \xB7 setting port to ${portSource}
2901
3071
  `);
2902
3072
  }
2903
3073