@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
@@ -73,39 +73,6 @@ function displayLocalProxyInfo(port) {
73
73
  var import_node_fs7 = __toESM(require("fs"), 1);
74
74
  var import_node_path8 = require("path");
75
75
 
76
- // src/config/overrides/constants.ts
77
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
78
- var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
79
-
80
- // src/config/overrides/is-override-cookie.ts
81
- function isOverrideCookie(cookie) {
82
- return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
83
- }
84
-
85
- // src/config/overrides/get-override-from-cookie.ts
86
- function getOverrideFromCookie(cookie) {
87
- if (!isOverrideCookie(cookie) || !cookie.value)
88
- return;
89
- return {
90
- application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
91
- host: cookie.value
92
- };
93
- }
94
-
95
- // src/config/overrides/parse-overrides.ts
96
- function parseOverrides(cookies) {
97
- const overridesConfig = { applications: {} };
98
- cookies.forEach((cookie) => {
99
- const override = getOverrideFromCookie(cookie);
100
- if (!override)
101
- return;
102
- overridesConfig.applications[override.application] = {
103
- environment: { host: override.host }
104
- };
105
- });
106
- return overridesConfig;
107
- }
108
-
109
76
  // src/config/errors.ts
110
77
  var MicrofrontendError = class extends Error {
111
78
  constructor(message, opts) {
@@ -198,253 +165,47 @@ var MicrofrontendError = class extends Error {
198
165
  }
199
166
  };
200
167
 
201
- // src/config/microfrontends-config/utils/get-config-from-env.ts
202
- function getConfigStringFromEnv() {
203
- const config = process.env.MFE_CONFIG;
204
- if (!config) {
205
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
206
- type: "config",
207
- subtype: "not_found_in_env"
208
- });
209
- }
210
- return config;
211
- }
212
-
213
- // src/config/schema/utils/is-default-app.ts
214
- function isDefaultApp(a) {
215
- return !("routing" in a);
216
- }
217
-
218
- // src/config/microfrontends/utils/find-repository-root.ts
219
- var import_node_fs = __toESM(require("fs"), 1);
220
- var import_node_path = __toESM(require("path"), 1);
221
- var GIT_DIRECTORY = ".git";
222
- function hasGitDirectory(dir) {
223
- const gitPath = import_node_path.default.join(dir, GIT_DIRECTORY);
224
- return import_node_fs.default.existsSync(gitPath) && import_node_fs.default.statSync(gitPath).isDirectory();
225
- }
226
- function hasPnpmWorkspaces(dir) {
227
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "pnpm-workspace.yaml"));
228
- }
229
- function hasPackageJson(dir) {
230
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "package.json"));
231
- }
232
- function findRepositoryRoot(startDir) {
233
- if (process.env.NX_WORKSPACE_ROOT) {
234
- return process.env.NX_WORKSPACE_ROOT;
235
- }
236
- let currentDir = startDir || process.cwd();
237
- let lastPackageJsonDir = null;
238
- while (currentDir !== import_node_path.default.parse(currentDir).root) {
239
- if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
240
- return currentDir;
241
- }
242
- if (hasPackageJson(currentDir)) {
243
- lastPackageJsonDir = currentDir;
244
- }
245
- currentDir = import_node_path.default.dirname(currentDir);
246
- }
247
- if (lastPackageJsonDir) {
248
- return lastPackageJsonDir;
249
- }
250
- throw new Error(
251
- `Could not find the root of the repository for ${startDir}. Please ensure that the directory is part of a Git repository. If you suspect that this should work, please file an issue to the Vercel team.`
252
- );
253
- }
254
-
255
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
256
- var import_node_path2 = require("path");
257
- var import_node_fs2 = require("fs");
168
+ // src/config/microfrontends-config/isomorphic/index.ts
258
169
  var import_jsonc_parser = require("jsonc-parser");
259
- var import_fast_glob = __toESM(require("fast-glob"), 1);
260
170
 
261
- // src/config/microfrontends/utils/get-config-file-name.ts
262
- var DEFAULT_CONFIGURATION_FILENAMES = [
263
- "microfrontends.json",
264
- "microfrontends.jsonc"
265
- ];
266
- function getPossibleConfigurationFilenames({
267
- customConfigFilename
268
- }) {
269
- if (customConfigFilename) {
270
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
271
- throw new Error(
272
- `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.`
273
- );
274
- }
275
- return Array.from(
276
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
277
- );
278
- }
279
- return DEFAULT_CONFIGURATION_FILENAMES;
280
- }
281
-
282
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
283
- var configCache = {};
284
- function findPackageWithMicrofrontendsConfig({
285
- repositoryRoot,
286
- applicationContext,
287
- customConfigFilename
288
- }) {
289
- const applicationName = applicationContext.name;
290
- try {
291
- const microfrontendsJsonPaths = import_fast_glob.default.globSync(
292
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
293
- {
294
- cwd: repositoryRoot,
295
- absolute: true,
296
- onlyFiles: true,
297
- followSymbolicLinks: false,
298
- ignore: ["**/node_modules/**", "**/.git/**"]
299
- }
300
- );
301
- const matchingPaths = [];
302
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
303
- try {
304
- const microfrontendsJsonContent = (0, import_node_fs2.readFileSync)(
305
- microfrontendsJsonPath,
306
- "utf-8"
307
- );
308
- const microfrontendsJson = (0, import_jsonc_parser.parse)(microfrontendsJsonContent);
309
- if (microfrontendsJson.applications[applicationName]) {
310
- matchingPaths.push(microfrontendsJsonPath);
311
- } else {
312
- for (const [_, app] of Object.entries(
313
- microfrontendsJson.applications
314
- )) {
315
- if (app.packageName === applicationName) {
316
- matchingPaths.push(microfrontendsJsonPath);
317
- }
318
- }
319
- }
320
- } catch (error2) {
321
- }
322
- }
323
- if (matchingPaths.length > 1) {
324
- throw new MicrofrontendError(
325
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
326
- ${matchingPaths.join("\n \u2022 ")}`,
327
- { type: "config", subtype: "inference_failed" }
328
- );
329
- }
330
- if (matchingPaths.length === 0) {
331
- let additionalErrorMessage = "";
332
- if (microfrontendsJsonPaths.length > 0) {
333
- if (!applicationContext.projectName) {
334
- additionalErrorMessage = `
335
-
336
- If the name in package.json (${applicationContext.packageJsonName}) differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`;
337
- } else {
338
- additionalErrorMessage = `
339
-
340
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
341
- }
342
- }
343
- throw new MicrofrontendError(
344
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
345
-
346
- If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable.
347
-
348
- If your Vercel Microfrontends configuration has a custom name, ensure the VC_MICROFRONTENDS_CONFIG_FILE_NAME environment variable is set, you can pull the vercel project environment variables using the "vercel env pull" command.
171
+ // src/config/overrides/constants.ts
172
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
173
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
349
174
 
350
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
351
- { type: "config", subtype: "inference_failed" }
352
- );
353
- }
354
- const [packageJsonPath] = matchingPaths;
355
- return (0, import_node_path2.dirname)(packageJsonPath);
356
- } catch (error2) {
357
- if (error2 instanceof MicrofrontendError) {
358
- throw error2;
359
- }
360
- return null;
361
- }
362
- }
363
- function inferMicrofrontendsLocation(opts) {
364
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
365
- if (configCache[cacheKey]) {
366
- return configCache[cacheKey];
367
- }
368
- const result = findPackageWithMicrofrontendsConfig(opts);
369
- if (!result) {
370
- throw new MicrofrontendError(
371
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
372
- { type: "config", subtype: "inference_failed" }
373
- );
374
- }
375
- configCache[cacheKey] = result;
376
- return result;
175
+ // src/config/overrides/is-override-cookie.ts
176
+ function isOverrideCookie(cookie) {
177
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
377
178
  }
378
179
 
379
- // src/config/microfrontends/utils/is-monorepo.ts
380
- var import_node_fs3 = __toESM(require("fs"), 1);
381
- var import_node_path3 = __toESM(require("path"), 1);
382
- function isMonorepo({
383
- repositoryRoot
384
- }) {
385
- try {
386
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
387
- return true;
388
- }
389
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "vlt-workspaces.json"))) {
390
- return true;
391
- }
392
- if (process.env.NX_WORKSPACE_ROOT === import_node_path3.default.resolve(repositoryRoot)) {
393
- return true;
394
- }
395
- const packageJsonPath = import_node_path3.default.join(repositoryRoot, "package.json");
396
- if (!import_node_fs3.default.existsSync(packageJsonPath)) {
397
- return false;
398
- }
399
- const packageJson = JSON.parse(
400
- import_node_fs3.default.readFileSync(packageJsonPath, "utf-8")
401
- );
402
- return packageJson.workspaces !== void 0;
403
- } catch (error2) {
404
- logger.error("Error determining if repository is a monorepo", error2);
405
- return false;
406
- }
180
+ // src/config/overrides/get-override-from-cookie.ts
181
+ function getOverrideFromCookie(cookie) {
182
+ if (!isOverrideCookie(cookie) || !cookie.value)
183
+ return;
184
+ return {
185
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
186
+ host: cookie.value
187
+ };
407
188
  }
408
189
 
409
- // src/config/microfrontends/utils/find-package-root.ts
410
- var import_node_fs4 = __toESM(require("fs"), 1);
411
- var import_node_path4 = __toESM(require("path"), 1);
412
- var PACKAGE_JSON = "package.json";
413
- function findPackageRoot(startDir) {
414
- let currentDir = startDir || process.cwd();
415
- while (currentDir !== import_node_path4.default.parse(currentDir).root) {
416
- const pkgJsonPath = import_node_path4.default.join(currentDir, PACKAGE_JSON);
417
- if (import_node_fs4.default.existsSync(pkgJsonPath)) {
418
- return currentDir;
419
- }
420
- currentDir = import_node_path4.default.dirname(currentDir);
421
- }
422
- throw new Error(
423
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
424
- );
190
+ // src/config/overrides/parse-overrides.ts
191
+ function parseOverrides(cookies) {
192
+ const overridesConfig = { applications: {} };
193
+ cookies.forEach((cookie) => {
194
+ const override = getOverrideFromCookie(cookie);
195
+ if (!override)
196
+ return;
197
+ overridesConfig.applications[override.application] = {
198
+ environment: { host: override.host }
199
+ };
200
+ });
201
+ return overridesConfig;
425
202
  }
426
203
 
427
- // src/config/microfrontends/utils/find-config.ts
428
- var import_node_fs5 = __toESM(require("fs"), 1);
429
- var import_node_path5 = require("path");
430
- function findConfig({
431
- dir,
432
- customConfigFilename
433
- }) {
434
- for (const filename of getPossibleConfigurationFilenames({
435
- customConfigFilename
436
- })) {
437
- const maybeConfig = (0, import_node_path5.join)(dir, filename);
438
- if (import_node_fs5.default.existsSync(maybeConfig)) {
439
- return maybeConfig;
440
- }
441
- }
442
- return null;
204
+ // src/config/schema/utils/is-default-app.ts
205
+ function isDefaultApp(a) {
206
+ return !("routing" in a);
443
207
  }
444
208
 
445
- // src/config/microfrontends-config/isomorphic/index.ts
446
- var import_jsonc_parser2 = require("jsonc-parser");
447
-
448
209
  // src/config/microfrontends-config/client/index.ts
449
210
  var import_path_to_regexp = require("path-to-regexp");
450
211
  var regexpCache = /* @__PURE__ */ new Map();
@@ -500,49 +261,226 @@ var MicrofrontendConfigClient = class {
500
261
  static fromEnv(config) {
501
262
  if (!config) {
502
263
  throw new Error(
503
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
264
+ "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
265
+ );
266
+ }
267
+ return new MicrofrontendConfigClient(JSON.parse(config));
268
+ }
269
+ isEqual(other) {
270
+ return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
271
+ }
272
+ getApplicationNameForPath(path6) {
273
+ if (!path6.startsWith("/")) {
274
+ throw new Error(`Path must start with a /`);
275
+ }
276
+ if (this.pathCache[path6]) {
277
+ return this.pathCache[path6];
278
+ }
279
+ const pathname = new URL(path6, "https://example.com").pathname;
280
+ for (const [name, application] of Object.entries(this.applications)) {
281
+ if (application.routing) {
282
+ for (const group of application.routing) {
283
+ for (const childPath of group.paths) {
284
+ const regexp = getRegexp(childPath);
285
+ if (regexp.test(pathname)) {
286
+ this.pathCache[path6] = name;
287
+ return name;
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ const defaultApplication = Object.entries(this.applications).find(
294
+ ([, application]) => application.default
295
+ );
296
+ if (!defaultApplication) {
297
+ return null;
298
+ }
299
+ this.pathCache[path6] = defaultApplication[0];
300
+ return defaultApplication[0];
301
+ }
302
+ serialize() {
303
+ return this.serialized;
304
+ }
305
+ };
306
+
307
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
308
+ function getConfigStringFromEnv() {
309
+ const config = process.env.MFE_CONFIG;
310
+ if (!config) {
311
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
312
+ type: "config",
313
+ subtype: "not_found_in_env"
314
+ });
315
+ }
316
+ return config;
317
+ }
318
+
319
+ // src/config/microfrontends-config/isomorphic/constants.ts
320
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
321
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
322
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
323
+
324
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
325
+ function generatePortFromName({
326
+ name,
327
+ minPort = 3e3,
328
+ maxPort = 8e3
329
+ }) {
330
+ if (!name) {
331
+ throw new Error("Name is required to generate a port");
332
+ }
333
+ let hash = 0;
334
+ for (let i = 0; i < name.length; i++) {
335
+ hash = (hash << 5) - hash + name.charCodeAt(i);
336
+ hash |= 0;
337
+ }
338
+ hash = Math.abs(hash);
339
+ const range = maxPort - minPort;
340
+ const port = minPort + hash % range;
341
+ return port;
342
+ }
343
+
344
+ // src/config/microfrontends-config/isomorphic/host.ts
345
+ var Host = class {
346
+ constructor(hostConfig, options) {
347
+ if (typeof hostConfig === "string") {
348
+ ({
349
+ protocol: this.protocol,
350
+ host: this.host,
351
+ port: this.port
352
+ } = Host.parseUrl(hostConfig));
353
+ } else {
354
+ const { protocol = "https", host, port } = hostConfig;
355
+ this.protocol = protocol;
356
+ this.host = host;
357
+ this.port = port;
358
+ }
359
+ this.local = options?.isLocal;
360
+ }
361
+ static parseUrl(url, defaultProtocol = "https") {
362
+ let hostToParse = url;
363
+ if (!/^https?:\/\//.exec(hostToParse)) {
364
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
365
+ }
366
+ const parsed = new URL(hostToParse);
367
+ if (!parsed.hostname) {
368
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
369
+ }
370
+ if (parsed.hash) {
371
+ throw new Error(
372
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
373
+ );
374
+ }
375
+ if (parsed.username || parsed.password) {
376
+ throw new Error(
377
+ Host.getMicrofrontendsError(
378
+ url,
379
+ "cannot have authentication credentials (username and/or password)"
380
+ )
381
+ );
382
+ }
383
+ if (parsed.pathname !== "/") {
384
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
385
+ }
386
+ if (parsed.search) {
387
+ throw new Error(
388
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
504
389
  );
505
390
  }
506
- return new MicrofrontendConfigClient(JSON.parse(config));
391
+ const protocol = parsed.protocol.slice(0, -1);
392
+ return {
393
+ protocol,
394
+ host: parsed.hostname,
395
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
396
+ };
507
397
  }
508
- isEqual(other) {
509
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
398
+ static getMicrofrontendsError(url, message) {
399
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
510
400
  }
511
- getApplicationNameForPath(path6) {
512
- if (!path6.startsWith("/")) {
513
- throw new Error(`Path must start with a /`);
514
- }
515
- if (this.pathCache[path6]) {
516
- return this.pathCache[path6];
517
- }
518
- const pathname = new URL(path6, "https://example.com").pathname;
519
- for (const [name, application] of Object.entries(this.applications)) {
520
- if (application.routing) {
521
- for (const group of application.routing) {
522
- for (const childPath of group.paths) {
523
- const regexp = getRegexp(childPath);
524
- if (regexp.test(pathname)) {
525
- this.pathCache[path6] = name;
526
- return name;
527
- }
528
- }
529
- }
401
+ isLocal() {
402
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
403
+ }
404
+ toString() {
405
+ const url = this.toUrl();
406
+ return url.toString().replace(/\/$/, "");
407
+ }
408
+ toUrl() {
409
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
410
+ return new URL(url);
411
+ }
412
+ };
413
+ var LocalHost = class extends Host {
414
+ constructor({
415
+ appName,
416
+ local
417
+ }) {
418
+ const portOverride = process.env[MFE_APP_PORT_ENV];
419
+ if (portOverride) {
420
+ const overridePort = Number.parseInt(portOverride, 10);
421
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
422
+ super({
423
+ protocol: "http",
424
+ host: "localhost",
425
+ port: overridePort
426
+ });
427
+ return;
530
428
  }
531
429
  }
532
- const defaultApplication = Object.entries(this.applications).find(
533
- ([, application]) => application.default
534
- );
535
- if (!defaultApplication) {
536
- return null;
430
+ let protocol;
431
+ let host;
432
+ let port;
433
+ if (typeof local === "number") {
434
+ port = local;
435
+ } else if (typeof local === "string") {
436
+ if (/^\d+$/.test(local)) {
437
+ port = Number.parseInt(local, 10);
438
+ } else {
439
+ const parsed = Host.parseUrl(local, "http");
440
+ protocol = parsed.protocol;
441
+ host = parsed.host;
442
+ port = parsed.port;
443
+ }
444
+ } else if (local) {
445
+ protocol = local.protocol;
446
+ host = local.host;
447
+ port = local.port;
537
448
  }
538
- this.pathCache[path6] = defaultApplication[0];
539
- return defaultApplication[0];
540
- }
541
- serialize() {
542
- return this.serialized;
449
+ super({
450
+ protocol: protocol ?? "http",
451
+ host: host ?? "localhost",
452
+ port: port ?? generatePortFromName({ name: appName })
453
+ });
543
454
  }
544
455
  };
545
456
 
457
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
458
+ var import_md5 = __toESM(require("md5"), 1);
459
+ function hashApplicationName(name) {
460
+ if (!name) {
461
+ throw new Error("Application name is required to generate hash");
462
+ }
463
+ return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
464
+ }
465
+
466
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
467
+ var PREFIX = "vc-ap";
468
+ function generateAssetPrefixFromName({
469
+ name
470
+ }) {
471
+ if (!name) {
472
+ throw new Error("Name is required to generate an asset prefix");
473
+ }
474
+ return `${PREFIX}-${hashApplicationName(name)}`;
475
+ }
476
+
477
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
478
+ function generateAutomationBypassEnvVarName({
479
+ name
480
+ }) {
481
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
482
+ }
483
+
546
484
  // src/config/microfrontends-config/isomorphic/validation.ts
547
485
  var import_path_to_regexp2 = require("path-to-regexp");
548
486
  var LIST_FORMATTER = new Intl.ListFormat("en", {
@@ -719,159 +657,11 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
719
657
  );
720
658
  throw new MicrofrontendError(
721
659
  `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
722
- { type: "config", subtype: "multiple_default_applications" }
723
- );
724
- }
725
- };
726
-
727
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
728
- var import_md5 = __toESM(require("md5"), 1);
729
- function hashApplicationName(name) {
730
- if (!name) {
731
- throw new Error("Application name is required to generate hash");
732
- }
733
- return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
734
- }
735
-
736
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
737
- var PREFIX = "vc-ap";
738
- function generateAssetPrefixFromName({
739
- name
740
- }) {
741
- if (!name) {
742
- throw new Error("Name is required to generate an asset prefix");
743
- }
744
- return `${PREFIX}-${hashApplicationName(name)}`;
745
- }
746
-
747
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
748
- function generatePortFromName({
749
- name,
750
- minPort = 3e3,
751
- maxPort = 8e3
752
- }) {
753
- if (!name) {
754
- throw new Error("Name is required to generate a port");
755
- }
756
- let hash = 0;
757
- for (let i = 0; i < name.length; i++) {
758
- hash = (hash << 5) - hash + name.charCodeAt(i);
759
- hash |= 0;
760
- }
761
- hash = Math.abs(hash);
762
- const range = maxPort - minPort;
763
- const port = minPort + hash % range;
764
- return port;
765
- }
766
-
767
- // src/config/microfrontends-config/isomorphic/host.ts
768
- var Host = class {
769
- constructor(hostConfig, options) {
770
- if (typeof hostConfig === "string") {
771
- ({
772
- protocol: this.protocol,
773
- host: this.host,
774
- port: this.port
775
- } = Host.parseUrl(hostConfig));
776
- } else {
777
- const { protocol = "https", host, port } = hostConfig;
778
- this.protocol = protocol;
779
- this.host = host;
780
- this.port = port;
781
- }
782
- this.local = options?.isLocal;
783
- }
784
- static parseUrl(url, defaultProtocol = "https") {
785
- let hostToParse = url;
786
- if (!/^https?:\/\//.exec(hostToParse)) {
787
- hostToParse = `${defaultProtocol}://${hostToParse}`;
788
- }
789
- const parsed = new URL(hostToParse);
790
- if (!parsed.hostname) {
791
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
792
- }
793
- if (parsed.hash) {
794
- throw new Error(
795
- Host.getMicrofrontendsError(url, "cannot have a fragment")
796
- );
797
- }
798
- if (parsed.username || parsed.password) {
799
- throw new Error(
800
- Host.getMicrofrontendsError(
801
- url,
802
- "cannot have authentication credentials (username and/or password)"
803
- )
804
- );
805
- }
806
- if (parsed.pathname !== "/") {
807
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
808
- }
809
- if (parsed.search) {
810
- throw new Error(
811
- Host.getMicrofrontendsError(url, "cannot have query parameters")
812
- );
813
- }
814
- const protocol = parsed.protocol.slice(0, -1);
815
- return {
816
- protocol,
817
- host: parsed.hostname,
818
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
819
- };
820
- }
821
- static getMicrofrontendsError(url, message) {
822
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
823
- }
824
- isLocal() {
825
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
826
- }
827
- toString() {
828
- const url = this.toUrl();
829
- return url.toString().replace(/\/$/, "");
830
- }
831
- toUrl() {
832
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
833
- return new URL(url);
834
- }
835
- };
836
- var LocalHost = class extends Host {
837
- constructor({
838
- appName,
839
- local
840
- }) {
841
- let protocol;
842
- let host;
843
- let port;
844
- if (typeof local === "number") {
845
- port = local;
846
- } else if (typeof local === "string") {
847
- if (/^\d+$/.test(local)) {
848
- port = Number.parseInt(local);
849
- } else {
850
- const parsed = Host.parseUrl(local, "http");
851
- protocol = parsed.protocol;
852
- host = parsed.host;
853
- port = parsed.port;
854
- }
855
- } else if (local) {
856
- protocol = local.protocol;
857
- host = local.host;
858
- port = local.port;
859
- }
860
- super({
861
- protocol: protocol ?? "http",
862
- host: host ?? "localhost",
863
- port: port ?? generatePortFromName({ name: appName })
864
- });
660
+ { type: "config", subtype: "multiple_default_applications" }
661
+ );
865
662
  }
866
663
  };
867
664
 
868
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
869
- function generateAutomationBypassEnvVarName({
870
- name
871
- }) {
872
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
873
- }
874
-
875
665
  // src/config/microfrontends-config/isomorphic/application.ts
876
666
  var Application = class {
877
667
  constructor(name, {
@@ -952,9 +742,6 @@ var ChildApplication = class extends Application {
952
742
  }
953
743
  };
954
744
 
955
- // src/config/microfrontends-config/isomorphic/constants.ts
956
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
957
-
958
745
  // src/config/microfrontends-config/isomorphic/index.ts
959
746
  var MicrofrontendConfigIsomorphic = class {
960
747
  constructor({
@@ -998,7 +785,7 @@ var MicrofrontendConfigIsomorphic = class {
998
785
  };
999
786
  }
1000
787
  static validate(config) {
1001
- const c = typeof config === "string" ? (0, import_jsonc_parser2.parse)(config) : config;
788
+ const c = typeof config === "string" ? (0, import_jsonc_parser.parse)(config) : config;
1002
789
  validateConfigPaths(c.applications);
1003
790
  validateConfigDefaultApplication(c.applications);
1004
791
  return c;
@@ -1007,7 +794,7 @@ var MicrofrontendConfigIsomorphic = class {
1007
794
  cookies
1008
795
  }) {
1009
796
  return new MicrofrontendConfigIsomorphic({
1010
- config: (0, import_jsonc_parser2.parse)(getConfigStringFromEnv()),
797
+ config: (0, import_jsonc_parser.parse)(getConfigStringFromEnv()),
1011
798
  overrides: parseOverrides(cookies ?? [])
1012
799
  });
1013
800
  }
@@ -1073,9 +860,17 @@ var MicrofrontendConfigIsomorphic = class {
1073
860
  return this.defaultApplication;
1074
861
  }
1075
862
  /**
1076
- * Returns the configured port for the local proxy
863
+ * Returns the configured port for the local proxy.
864
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
1077
865
  */
1078
866
  getLocalProxyPort() {
867
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
868
+ if (portOverride) {
869
+ const port = Number.parseInt(portOverride, 10);
870
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
871
+ return port;
872
+ }
873
+ }
1079
874
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1080
875
  }
1081
876
  toClientConfig(options) {
@@ -1113,32 +908,144 @@ var MicrofrontendConfigIsomorphic = class {
1113
908
  }
1114
909
  };
1115
910
 
911
+ // src/config/microfrontends/utils/find-config.ts
912
+ var import_node_fs = __toESM(require("fs"), 1);
913
+ var import_node_path = require("path");
914
+
915
+ // src/config/microfrontends/utils/get-config-file-name.ts
916
+ var DEFAULT_CONFIGURATION_FILENAMES = [
917
+ "microfrontends.json",
918
+ "microfrontends.jsonc"
919
+ ];
920
+ function getPossibleConfigurationFilenames({
921
+ customConfigFilename
922
+ }) {
923
+ if (customConfigFilename) {
924
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
925
+ throw new Error(
926
+ `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.`
927
+ );
928
+ }
929
+ return Array.from(
930
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
931
+ );
932
+ }
933
+ return DEFAULT_CONFIGURATION_FILENAMES;
934
+ }
935
+
936
+ // src/config/microfrontends/utils/find-config.ts
937
+ function findConfig({
938
+ dir,
939
+ customConfigFilename
940
+ }) {
941
+ for (const filename of getPossibleConfigurationFilenames({
942
+ customConfigFilename
943
+ })) {
944
+ const maybeConfig = (0, import_node_path.join)(dir, filename);
945
+ if (import_node_fs.default.existsSync(maybeConfig)) {
946
+ return maybeConfig;
947
+ }
948
+ }
949
+ return null;
950
+ }
951
+
952
+ // src/config/microfrontends/utils/find-package-root.ts
953
+ var import_node_fs2 = __toESM(require("fs"), 1);
954
+ var import_node_path2 = __toESM(require("path"), 1);
955
+ var PACKAGE_JSON = "package.json";
956
+ function findPackageRoot(startDir) {
957
+ let currentDir = startDir || process.cwd();
958
+ while (currentDir !== import_node_path2.default.parse(currentDir).root) {
959
+ const pkgJsonPath = import_node_path2.default.join(currentDir, PACKAGE_JSON);
960
+ if (import_node_fs2.default.existsSync(pkgJsonPath)) {
961
+ return currentDir;
962
+ }
963
+ currentDir = import_node_path2.default.dirname(currentDir);
964
+ }
965
+ throw new Error(
966
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
967
+ );
968
+ }
969
+
970
+ // src/config/microfrontends/utils/find-repository-root.ts
971
+ var import_node_fs3 = __toESM(require("fs"), 1);
972
+ var import_node_path3 = __toESM(require("path"), 1);
973
+ var GIT_DIRECTORY = ".git";
974
+ function hasGitDirectory(dir) {
975
+ const gitPath = import_node_path3.default.join(dir, GIT_DIRECTORY);
976
+ return import_node_fs3.default.existsSync(gitPath) && import_node_fs3.default.statSync(gitPath).isDirectory();
977
+ }
978
+ function hasPnpmWorkspaces(dir) {
979
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "pnpm-workspace.yaml"));
980
+ }
981
+ function hasPackageJson(dir) {
982
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "package.json"));
983
+ }
984
+ function findRepositoryRoot(startDir) {
985
+ if (process.env.NX_WORKSPACE_ROOT) {
986
+ return process.env.NX_WORKSPACE_ROOT;
987
+ }
988
+ let currentDir = startDir || process.cwd();
989
+ let lastPackageJsonDir = null;
990
+ while (currentDir !== import_node_path3.default.parse(currentDir).root) {
991
+ if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
992
+ return currentDir;
993
+ }
994
+ if (hasPackageJson(currentDir)) {
995
+ lastPackageJsonDir = currentDir;
996
+ }
997
+ currentDir = import_node_path3.default.dirname(currentDir);
998
+ }
999
+ if (lastPackageJsonDir) {
1000
+ return lastPackageJsonDir;
1001
+ }
1002
+ throw new Error(
1003
+ `Could not find the root of the repository for ${startDir}. Please ensure that the directory is part of a Git repository. If you suspect that this should work, please file an issue to the Vercel team.`
1004
+ );
1005
+ }
1006
+
1116
1007
  // src/config/microfrontends/utils/get-application-context.ts
1117
- var import_node_fs6 = __toESM(require("fs"), 1);
1118
- var import_node_path6 = __toESM(require("path"), 1);
1008
+ var import_node_fs4 = __toESM(require("fs"), 1);
1009
+ var import_node_path4 = __toESM(require("path"), 1);
1119
1010
  function getApplicationContext(opts) {
1120
1011
  if (opts?.appName) {
1012
+ logger.debug(
1013
+ "[MFE Config] Application name from appName parameter:",
1014
+ opts.appName
1015
+ );
1121
1016
  return { name: opts.appName };
1122
1017
  }
1123
1018
  if (process.env.VERCEL_PROJECT_NAME) {
1019
+ logger.debug(
1020
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
1021
+ process.env.VERCEL_PROJECT_NAME
1022
+ );
1124
1023
  return {
1125
1024
  name: process.env.VERCEL_PROJECT_NAME,
1126
1025
  projectName: process.env.VERCEL_PROJECT_NAME
1127
1026
  };
1128
1027
  }
1129
1028
  if (process.env.NX_TASK_TARGET_PROJECT) {
1029
+ logger.debug(
1030
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
1031
+ process.env.NX_TASK_TARGET_PROJECT
1032
+ );
1130
1033
  return {
1131
1034
  name: process.env.NX_TASK_TARGET_PROJECT,
1132
1035
  packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1133
1036
  };
1134
1037
  }
1135
1038
  try {
1136
- const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1137
- import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1039
+ const vercelProjectJsonPath = import_node_fs4.default.readFileSync(
1040
+ import_node_path4.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1138
1041
  "utf-8"
1139
1042
  );
1140
1043
  const projectJson = JSON.parse(vercelProjectJsonPath);
1141
1044
  if (projectJson.projectName) {
1045
+ logger.debug(
1046
+ "[MFE Config] Application name from .vercel/project.json:",
1047
+ projectJson.projectName
1048
+ );
1142
1049
  return {
1143
1050
  name: projectJson.projectName,
1144
1051
  projectName: projectJson.projectName
@@ -1147,8 +1054,8 @@ function getApplicationContext(opts) {
1147
1054
  } catch (_) {
1148
1055
  }
1149
1056
  try {
1150
- const packageJsonString = import_node_fs6.default.readFileSync(
1151
- import_node_path6.default.join(opts?.packageRoot || ".", "package.json"),
1057
+ const packageJsonString = import_node_fs4.default.readFileSync(
1058
+ import_node_path4.default.join(opts?.packageRoot || ".", "package.json"),
1152
1059
  "utf-8"
1153
1060
  );
1154
1061
  const packageJson = JSON.parse(packageJsonString);
@@ -1162,6 +1069,10 @@ function getApplicationContext(opts) {
1162
1069
  }
1163
1070
  );
1164
1071
  }
1072
+ logger.debug(
1073
+ "[MFE Config] Application name from package.json:",
1074
+ packageJson.name
1075
+ );
1165
1076
  return { name: packageJson.name, packageJsonName: packageJson.name };
1166
1077
  } catch (err) {
1167
1078
  throw MicrofrontendError.handle(err, {
@@ -1170,6 +1081,209 @@ function getApplicationContext(opts) {
1170
1081
  }
1171
1082
  }
1172
1083
 
1084
+ // src/config/microfrontends/utils/infer-microfrontends-location.ts
1085
+ var import_node_fs5 = require("fs");
1086
+ var import_node_path5 = require("path");
1087
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
1088
+ var import_jsonc_parser2 = require("jsonc-parser");
1089
+ var configCache = {};
1090
+ function findPackageWithMicrofrontendsConfig({
1091
+ repositoryRoot,
1092
+ applicationContext,
1093
+ customConfigFilename
1094
+ }) {
1095
+ const applicationName = applicationContext.name;
1096
+ logger.debug(
1097
+ "[MFE Config] Searching repository for configs containing application:",
1098
+ applicationName
1099
+ );
1100
+ try {
1101
+ const microfrontendsJsonPaths = import_fast_glob.default.globSync(
1102
+ `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
1103
+ {
1104
+ cwd: repositoryRoot,
1105
+ absolute: true,
1106
+ onlyFiles: true,
1107
+ followSymbolicLinks: false,
1108
+ ignore: ["**/node_modules/**", "**/.git/**"]
1109
+ }
1110
+ );
1111
+ logger.debug(
1112
+ "[MFE Config] Found",
1113
+ microfrontendsJsonPaths.length,
1114
+ "config file(s) in repository"
1115
+ );
1116
+ const matchingPaths = [];
1117
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1118
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1119
+ matchingPaths.push(microfrontendsJsonPath);
1120
+ }
1121
+ }
1122
+ logger.debug(
1123
+ "[MFE Config] Total matching config files:",
1124
+ matchingPaths.length
1125
+ );
1126
+ if (matchingPaths.length > 1) {
1127
+ throw new MicrofrontendError(
1128
+ `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
1129
+ ${matchingPaths.join("\n \u2022 ")}`,
1130
+ { type: "config", subtype: "inference_failed" }
1131
+ );
1132
+ }
1133
+ if (matchingPaths.length === 0) {
1134
+ if (repositoryRoot && doesMisplacedConfigExist(
1135
+ repositoryRoot,
1136
+ applicationName,
1137
+ customConfigFilename
1138
+ )) {
1139
+ logger.debug(
1140
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1141
+ );
1142
+ const misplacedConfigPath = (0, import_node_path5.join)(
1143
+ repositoryRoot,
1144
+ ".vercel",
1145
+ customConfigFilename || "microfrontends.json"
1146
+ );
1147
+ throw new MicrofrontendError(
1148
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1149
+
1150
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1151
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1152
+
1153
+ To fix this:
1154
+ 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
1155
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1156
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1157
+
1158
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1159
+ { type: "config", subtype: "inference_failed" }
1160
+ );
1161
+ }
1162
+ let additionalErrorMessage = "";
1163
+ if (microfrontendsJsonPaths.length > 0) {
1164
+ if (!applicationContext.projectName) {
1165
+ additionalErrorMessage = `
1166
+
1167
+ If the name in package.json (${applicationContext.packageJsonName}) differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`;
1168
+ } else {
1169
+ additionalErrorMessage = `
1170
+
1171
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1172
+ }
1173
+ }
1174
+ throw new MicrofrontendError(
1175
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1176
+
1177
+ If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable.
1178
+
1179
+ If your Vercel Microfrontends configuration has a custom name, ensure the VC_MICROFRONTENDS_CONFIG_FILE_NAME environment variable is set, you can pull the vercel project environment variables using the "vercel env pull" command.
1180
+
1181
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1182
+ { type: "config", subtype: "inference_failed" }
1183
+ );
1184
+ }
1185
+ const [packageJsonPath] = matchingPaths;
1186
+ return (0, import_node_path5.dirname)(packageJsonPath);
1187
+ } catch (error2) {
1188
+ if (error2 instanceof MicrofrontendError) {
1189
+ throw error2;
1190
+ }
1191
+ return null;
1192
+ }
1193
+ }
1194
+ function inferMicrofrontendsLocation(opts) {
1195
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
1196
+ if (configCache[cacheKey]) {
1197
+ return configCache[cacheKey];
1198
+ }
1199
+ const result = findPackageWithMicrofrontendsConfig(opts);
1200
+ if (!result) {
1201
+ throw new MicrofrontendError(
1202
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1203
+ { type: "config", subtype: "inference_failed" }
1204
+ );
1205
+ }
1206
+ configCache[cacheKey] = result;
1207
+ return result;
1208
+ }
1209
+ function existsSync(path6) {
1210
+ try {
1211
+ (0, import_node_fs5.statSync)(path6);
1212
+ return true;
1213
+ } catch (_) {
1214
+ return false;
1215
+ }
1216
+ }
1217
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1218
+ logger.debug(
1219
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1220
+ );
1221
+ const misplacedConfigPath = (0, import_node_path5.join)(
1222
+ repositoryRoot,
1223
+ ".vercel",
1224
+ customConfigFilename || "microfrontends.json"
1225
+ );
1226
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1227
+ }
1228
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1229
+ try {
1230
+ const microfrontendsJsonContent = (0, import_node_fs5.readFileSync)(
1231
+ microfrontendsJsonPath,
1232
+ "utf-8"
1233
+ );
1234
+ const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
1235
+ if (microfrontendsJson.applications[applicationName]) {
1236
+ logger.debug(
1237
+ "[MFE Config] Found application in config:",
1238
+ microfrontendsJsonPath
1239
+ );
1240
+ return true;
1241
+ }
1242
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1243
+ if (app.packageName === applicationName) {
1244
+ logger.debug(
1245
+ "[MFE Config] Found application via packageName in config:",
1246
+ microfrontendsJsonPath
1247
+ );
1248
+ return true;
1249
+ }
1250
+ }
1251
+ } catch (error2) {
1252
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1253
+ }
1254
+ return false;
1255
+ }
1256
+
1257
+ // src/config/microfrontends/utils/is-monorepo.ts
1258
+ var import_node_fs6 = __toESM(require("fs"), 1);
1259
+ var import_node_path6 = __toESM(require("path"), 1);
1260
+ function isMonorepo({
1261
+ repositoryRoot
1262
+ }) {
1263
+ try {
1264
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1265
+ return true;
1266
+ }
1267
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "vlt-workspaces.json"))) {
1268
+ return true;
1269
+ }
1270
+ if (process.env.NX_WORKSPACE_ROOT === import_node_path6.default.resolve(repositoryRoot)) {
1271
+ return true;
1272
+ }
1273
+ const packageJsonPath = import_node_path6.default.join(repositoryRoot, "package.json");
1274
+ if (!import_node_fs6.default.existsSync(packageJsonPath)) {
1275
+ return false;
1276
+ }
1277
+ const packageJson = JSON.parse(
1278
+ import_node_fs6.default.readFileSync(packageJsonPath, "utf-8")
1279
+ );
1280
+ return packageJson.workspaces !== void 0;
1281
+ } catch (error2) {
1282
+ logger.error("Error determining if repository is a monorepo", error2);
1283
+ return false;
1284
+ }
1285
+ }
1286
+
1173
1287
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1174
1288
  var import_node_path7 = __toESM(require("path"), 1);
1175
1289
 
@@ -1183,8 +1297,8 @@ function getOutputFilePath() {
1183
1297
  }
1184
1298
 
1185
1299
  // src/config/microfrontends/server/validation.ts
1186
- var import_jsonc_parser3 = require("jsonc-parser");
1187
1300
  var import_ajv = require("ajv");
1301
+ var import_jsonc_parser3 = require("jsonc-parser");
1188
1302
 
1189
1303
  // schema/schema.json
1190
1304
  var schema_default = {
@@ -1212,9 +1326,7 @@ var schema_default = {
1212
1326
  description: "Optional configuration options for the microfrontend."
1213
1327
  }
1214
1328
  },
1215
- required: [
1216
- "applications"
1217
- ],
1329
+ required: ["applications"],
1218
1330
  additionalProperties: false,
1219
1331
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1220
1332
  },
@@ -1251,19 +1363,14 @@ var schema_default = {
1251
1363
  description: "Development configuration for the default application."
1252
1364
  }
1253
1365
  },
1254
- required: [
1255
- "development"
1256
- ],
1366
+ required: ["development"],
1257
1367
  additionalProperties: false
1258
1368
  },
1259
1369
  DefaultDevelopment: {
1260
1370
  type: "object",
1261
1371
  properties: {
1262
1372
  local: {
1263
- type: [
1264
- "number",
1265
- "string"
1266
- ],
1373
+ type: ["number", "string"],
1267
1374
  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."
1268
1375
  },
1269
1376
  task: {
@@ -1275,9 +1382,7 @@ var schema_default = {
1275
1382
  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."
1276
1383
  }
1277
1384
  },
1278
- required: [
1279
- "fallback"
1280
- ],
1385
+ required: ["fallback"],
1281
1386
  additionalProperties: false
1282
1387
  },
1283
1388
  ChildApplication: {
@@ -1300,19 +1405,14 @@ var schema_default = {
1300
1405
  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."
1301
1406
  }
1302
1407
  },
1303
- required: [
1304
- "routing"
1305
- ],
1408
+ required: ["routing"],
1306
1409
  additionalProperties: false
1307
1410
  },
1308
1411
  ChildDevelopment: {
1309
1412
  type: "object",
1310
1413
  properties: {
1311
1414
  local: {
1312
- type: [
1313
- "number",
1314
- "string"
1315
- ],
1415
+ type: ["number", "string"],
1316
1416
  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."
1317
1417
  },
1318
1418
  task: {
@@ -1352,9 +1452,7 @@ var schema_default = {
1352
1452
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1353
1453
  }
1354
1454
  },
1355
- required: [
1356
- "paths"
1357
- ],
1455
+ required: ["paths"],
1358
1456
  additionalProperties: false,
1359
1457
  description: "A group of paths that is routed to this application."
1360
1458
  },
@@ -1533,7 +1631,13 @@ var MicrofrontendsServer = class {
1533
1631
  filePath,
1534
1632
  cookies
1535
1633
  } = {}) {
1634
+ logger.debug("[MFE Config] Starting config inference", {
1635
+ appName,
1636
+ directory: directory || process.cwd(),
1637
+ filePath
1638
+ });
1536
1639
  if (filePath) {
1640
+ logger.debug("[MFE Config] Using explicit filePath:", filePath);
1537
1641
  return MicrofrontendsServer.fromFile({
1538
1642
  filePath,
1539
1643
  cookies
@@ -1541,16 +1645,25 @@ var MicrofrontendsServer = class {
1541
1645
  }
1542
1646
  try {
1543
1647
  const packageRoot = findPackageRoot(directory);
1648
+ logger.debug("[MFE Config] Package root:", packageRoot);
1544
1649
  const applicationContext = getApplicationContext({
1545
1650
  appName,
1546
1651
  packageRoot
1547
1652
  });
1653
+ logger.debug("[MFE Config] Application context:", applicationContext);
1548
1654
  const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
1655
+ if (customConfigFilename) {
1656
+ logger.debug(
1657
+ "[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
1658
+ customConfigFilename
1659
+ );
1660
+ }
1549
1661
  const maybeConfig = findConfig({
1550
1662
  dir: packageRoot,
1551
1663
  customConfigFilename
1552
1664
  });
1553
1665
  if (maybeConfig) {
1666
+ logger.debug("[MFE Config] Config found at package root:", maybeConfig);
1554
1667
  return MicrofrontendsServer.fromFile({
1555
1668
  filePath: maybeConfig,
1556
1669
  cookies
@@ -1558,42 +1671,78 @@ var MicrofrontendsServer = class {
1558
1671
  }
1559
1672
  const repositoryRoot = findRepositoryRoot();
1560
1673
  const isMonorepo2 = isMonorepo({ repositoryRoot });
1674
+ logger.debug(
1675
+ "[MFE Config] Repository root:",
1676
+ repositoryRoot,
1677
+ "Is monorepo:",
1678
+ isMonorepo2
1679
+ );
1561
1680
  const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
1562
1681
  if (typeof configFromEnv === "string") {
1682
+ logger.debug(
1683
+ "[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
1684
+ configFromEnv
1685
+ );
1563
1686
  const maybeConfigFromEnv = (0, import_node_path8.resolve)(packageRoot, configFromEnv);
1564
1687
  if (maybeConfigFromEnv) {
1688
+ logger.debug(
1689
+ "[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
1690
+ maybeConfigFromEnv
1691
+ );
1565
1692
  return MicrofrontendsServer.fromFile({
1566
1693
  filePath: maybeConfigFromEnv,
1567
1694
  cookies
1568
1695
  });
1569
1696
  }
1570
1697
  } else {
1698
+ const vercelDir = (0, import_node_path8.join)(packageRoot, ".vercel");
1699
+ logger.debug(
1700
+ "[MFE Config] Searching for config in .vercel directory:",
1701
+ vercelDir
1702
+ );
1571
1703
  const maybeConfigFromVercel = findConfig({
1572
- dir: (0, import_node_path8.join)(packageRoot, ".vercel"),
1704
+ dir: vercelDir,
1573
1705
  customConfigFilename
1574
1706
  });
1575
1707
  if (maybeConfigFromVercel) {
1708
+ logger.debug(
1709
+ "[MFE Config] Config found in .vercel directory:",
1710
+ maybeConfigFromVercel
1711
+ );
1576
1712
  return MicrofrontendsServer.fromFile({
1577
1713
  filePath: maybeConfigFromVercel,
1578
1714
  cookies
1579
1715
  });
1580
1716
  }
1581
1717
  if (isMonorepo2) {
1718
+ logger.debug(
1719
+ "[MFE Config] Inferring microfrontends location in monorepo for application:",
1720
+ applicationContext.name
1721
+ );
1582
1722
  const defaultPackage = inferMicrofrontendsLocation({
1583
1723
  repositoryRoot,
1584
1724
  applicationContext,
1585
1725
  customConfigFilename
1586
1726
  });
1727
+ logger.debug(
1728
+ "[MFE Config] Inferred package location:",
1729
+ defaultPackage
1730
+ );
1587
1731
  const maybeConfigFromDefault = findConfig({
1588
1732
  dir: defaultPackage,
1589
1733
  customConfigFilename
1590
1734
  });
1591
1735
  if (maybeConfigFromDefault) {
1736
+ logger.debug(
1737
+ "[MFE Config] Config found in inferred package:",
1738
+ maybeConfigFromDefault
1739
+ );
1592
1740
  return MicrofrontendsServer.fromFile({
1593
1741
  filePath: maybeConfigFromDefault,
1594
1742
  cookies
1595
1743
  });
1596
1744
  }
1745
+ logger.debug("[MFE Config] No config found in inferred package");
1597
1746
  }
1598
1747
  }
1599
1748
  throw new MicrofrontendError(
@@ -1619,8 +1768,13 @@ var MicrofrontendsServer = class {
1619
1768
  cookies
1620
1769
  }) {
1621
1770
  try {
1771
+ logger.debug("[MFE Config] Reading config from file:", filePath);
1622
1772
  const configJson = import_node_fs7.default.readFileSync(filePath, "utf-8");
1623
1773
  const config = MicrofrontendsServer.validate(configJson);
1774
+ logger.debug(
1775
+ "[MFE Config] Config loaded with applications:",
1776
+ Object.keys(config.applications)
1777
+ );
1624
1778
  return new MicrofrontendsServer({
1625
1779
  config,
1626
1780
  overrides: cookies ? parseOverrides(cookies) : void 0