@vercel/microfrontends 2.2.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +483 -394
  4. package/dist/config.cjs +191 -169
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +3 -2
  7. package/dist/config.js +192 -170
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +583 -511
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +589 -517
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +605 -533
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +614 -542
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +601 -529
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +607 -535
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +101 -50
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +4 -4
  25. package/dist/microfrontends/utils.js +102 -51
  26. package/dist/microfrontends/utils.js.map +1 -1
  27. package/dist/next/client.cjs +1 -1
  28. package/dist/next/client.cjs.map +1 -1
  29. package/dist/next/client.d.ts +8 -8
  30. package/dist/next/client.js +1 -1
  31. package/dist/next/client.js.map +1 -1
  32. package/dist/next/config.cjs +723 -647
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +720 -644
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +244 -222
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +245 -223
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +192 -170
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +1 -1
  43. package/dist/next/testing.js +193 -171
  44. package/dist/next/testing.js.map +1 -1
  45. package/dist/overrides.cjs +5 -5
  46. package/dist/overrides.cjs.map +1 -1
  47. package/dist/overrides.d.ts +9 -9
  48. package/dist/overrides.js +5 -5
  49. package/dist/overrides.js.map +1 -1
  50. package/dist/utils/mfe-port.cjs +620 -533
  51. package/dist/utils/mfe-port.cjs.map +1 -1
  52. package/dist/utils/mfe-port.d.ts +9 -1
  53. package/dist/utils/mfe-port.js +632 -546
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +8 -24
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.js +8 -24
  58. package/dist/validation.js.map +1 -1
  59. package/package.json +4 -6
@@ -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,274 +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
-
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
170
 
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
- logger.debug(
291
- "[MFE Config] Searching repository for configs containing application:",
292
- applicationName
293
- );
294
- try {
295
- const microfrontendsJsonPaths = import_fast_glob.default.globSync(
296
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
297
- {
298
- cwd: repositoryRoot,
299
- absolute: true,
300
- onlyFiles: true,
301
- followSymbolicLinks: false,
302
- ignore: ["**/node_modules/**", "**/.git/**"]
303
- }
304
- );
305
- logger.debug(
306
- "[MFE Config] Found",
307
- microfrontendsJsonPaths.length,
308
- "config file(s) in repository"
309
- );
310
- const matchingPaths = [];
311
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
312
- try {
313
- const microfrontendsJsonContent = (0, import_node_fs2.readFileSync)(
314
- microfrontendsJsonPath,
315
- "utf-8"
316
- );
317
- const microfrontendsJson = (0, import_jsonc_parser.parse)(microfrontendsJsonContent);
318
- if (microfrontendsJson.applications[applicationName]) {
319
- logger.debug(
320
- "[MFE Config] Found application in config:",
321
- microfrontendsJsonPath
322
- );
323
- matchingPaths.push(microfrontendsJsonPath);
324
- } else {
325
- for (const [_, app] of Object.entries(
326
- microfrontendsJson.applications
327
- )) {
328
- if (app.packageName === applicationName) {
329
- logger.debug(
330
- "[MFE Config] Found application via packageName in config:",
331
- microfrontendsJsonPath
332
- );
333
- matchingPaths.push(microfrontendsJsonPath);
334
- }
335
- }
336
- }
337
- } catch (error2) {
338
- }
339
- }
340
- logger.debug(
341
- "[MFE Config] Total matching config files:",
342
- matchingPaths.length
343
- );
344
- if (matchingPaths.length > 1) {
345
- throw new MicrofrontendError(
346
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
347
- ${matchingPaths.join("\n \u2022 ")}`,
348
- { type: "config", subtype: "inference_failed" }
349
- );
350
- }
351
- if (matchingPaths.length === 0) {
352
- let additionalErrorMessage = "";
353
- if (microfrontendsJsonPaths.length > 0) {
354
- if (!applicationContext.projectName) {
355
- additionalErrorMessage = `
356
-
357
- 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.`;
358
- } else {
359
- additionalErrorMessage = `
360
-
361
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
362
- }
363
- }
364
- throw new MicrofrontendError(
365
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
366
-
367
- 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.
368
-
369
- 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:`;
370
174
 
371
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
372
- { type: "config", subtype: "inference_failed" }
373
- );
374
- }
375
- const [packageJsonPath] = matchingPaths;
376
- return (0, import_node_path2.dirname)(packageJsonPath);
377
- } catch (error2) {
378
- if (error2 instanceof MicrofrontendError) {
379
- throw error2;
380
- }
381
- return null;
382
- }
383
- }
384
- function inferMicrofrontendsLocation(opts) {
385
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
386
- if (configCache[cacheKey]) {
387
- return configCache[cacheKey];
388
- }
389
- const result = findPackageWithMicrofrontendsConfig(opts);
390
- if (!result) {
391
- throw new MicrofrontendError(
392
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
393
- { type: "config", subtype: "inference_failed" }
394
- );
395
- }
396
- configCache[cacheKey] = result;
397
- return result;
175
+ // src/config/overrides/is-override-cookie.ts
176
+ function isOverrideCookie(cookie) {
177
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
398
178
  }
399
179
 
400
- // src/config/microfrontends/utils/is-monorepo.ts
401
- var import_node_fs3 = __toESM(require("fs"), 1);
402
- var import_node_path3 = __toESM(require("path"), 1);
403
- function isMonorepo({
404
- repositoryRoot
405
- }) {
406
- try {
407
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
408
- return true;
409
- }
410
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "vlt-workspaces.json"))) {
411
- return true;
412
- }
413
- if (process.env.NX_WORKSPACE_ROOT === import_node_path3.default.resolve(repositoryRoot)) {
414
- return true;
415
- }
416
- const packageJsonPath = import_node_path3.default.join(repositoryRoot, "package.json");
417
- if (!import_node_fs3.default.existsSync(packageJsonPath)) {
418
- return false;
419
- }
420
- const packageJson = JSON.parse(
421
- import_node_fs3.default.readFileSync(packageJsonPath, "utf-8")
422
- );
423
- return packageJson.workspaces !== void 0;
424
- } catch (error2) {
425
- logger.error("Error determining if repository is a monorepo", error2);
426
- return false;
427
- }
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
+ };
428
188
  }
429
189
 
430
- // src/config/microfrontends/utils/find-package-root.ts
431
- var import_node_fs4 = __toESM(require("fs"), 1);
432
- var import_node_path4 = __toESM(require("path"), 1);
433
- var PACKAGE_JSON = "package.json";
434
- function findPackageRoot(startDir) {
435
- let currentDir = startDir || process.cwd();
436
- while (currentDir !== import_node_path4.default.parse(currentDir).root) {
437
- const pkgJsonPath = import_node_path4.default.join(currentDir, PACKAGE_JSON);
438
- if (import_node_fs4.default.existsSync(pkgJsonPath)) {
439
- return currentDir;
440
- }
441
- currentDir = import_node_path4.default.dirname(currentDir);
442
- }
443
- throw new Error(
444
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
445
- );
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;
446
202
  }
447
203
 
448
- // src/config/microfrontends/utils/find-config.ts
449
- var import_node_fs5 = __toESM(require("fs"), 1);
450
- var import_node_path5 = require("path");
451
- function findConfig({
452
- dir,
453
- customConfigFilename
454
- }) {
455
- for (const filename of getPossibleConfigurationFilenames({
456
- customConfigFilename
457
- })) {
458
- const maybeConfig = (0, import_node_path5.join)(dir, filename);
459
- if (import_node_fs5.default.existsSync(maybeConfig)) {
460
- return maybeConfig;
461
- }
462
- }
463
- return null;
204
+ // src/config/schema/utils/is-default-app.ts
205
+ function isDefaultApp(a) {
206
+ return !("routing" in a);
464
207
  }
465
208
 
466
- // src/config/microfrontends-config/isomorphic/index.ts
467
- var import_jsonc_parser2 = require("jsonc-parser");
468
-
469
209
  // src/config/microfrontends-config/client/index.ts
470
210
  var import_path_to_regexp = require("path-to-regexp");
471
211
  var regexpCache = /* @__PURE__ */ new Map();
@@ -526,44 +266,221 @@ var MicrofrontendConfigClient = class {
526
266
  }
527
267
  return new MicrofrontendConfigClient(JSON.parse(config));
528
268
  }
529
- isEqual(other) {
530
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
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")
389
+ );
390
+ }
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
+ };
397
+ }
398
+ static getMicrofrontendsError(url, message) {
399
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
400
+ }
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);
531
411
  }
532
- getApplicationNameForPath(path6) {
533
- if (!path6.startsWith("/")) {
534
- throw new Error(`Path must start with a /`);
535
- }
536
- if (this.pathCache[path6]) {
537
- return this.pathCache[path6];
538
- }
539
- const pathname = new URL(path6, "https://example.com").pathname;
540
- for (const [name, application] of Object.entries(this.applications)) {
541
- if (application.routing) {
542
- for (const group of application.routing) {
543
- for (const childPath of group.paths) {
544
- const regexp = getRegexp(childPath);
545
- if (regexp.test(pathname)) {
546
- this.pathCache[path6] = name;
547
- return name;
548
- }
549
- }
550
- }
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;
551
428
  }
552
429
  }
553
- const defaultApplication = Object.entries(this.applications).find(
554
- ([, application]) => application.default
555
- );
556
- if (!defaultApplication) {
557
- 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;
558
448
  }
559
- this.pathCache[path6] = defaultApplication[0];
560
- return defaultApplication[0];
561
- }
562
- serialize() {
563
- return this.serialized;
449
+ super({
450
+ protocol: protocol ?? "http",
451
+ host: host ?? "localhost",
452
+ port: port ?? generatePortFromName({ name: appName })
453
+ });
564
454
  }
565
455
  };
566
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
+
567
484
  // src/config/microfrontends-config/isomorphic/validation.ts
568
485
  var import_path_to_regexp2 = require("path-to-regexp");
569
486
  var LIST_FORMATTER = new Intl.ListFormat("en", {
@@ -719,180 +636,32 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
719
636
  if (!applicationConfigsById) {
720
637
  return;
721
638
  }
722
- const applicationsWithoutRouting = Object.entries(
723
- applicationConfigsById
724
- ).filter(([, app]) => isDefaultApp(app));
725
- const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
726
- (acc) => {
727
- return acc + 1;
728
- },
729
- 0
730
- );
731
- if (numApplicationsWithoutRouting === 0) {
732
- throw new MicrofrontendError(
733
- "No default application found. At least one application needs to be the default by omitting routing.",
734
- { type: "config", subtype: "no_default_application" }
735
- );
736
- }
737
- if (numApplicationsWithoutRouting > 1) {
738
- const applicationNamesMissingRouting = applicationsWithoutRouting.map(
739
- ([name]) => name
740
- );
741
- throw new MicrofrontendError(
742
- `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
743
- { type: "config", subtype: "multiple_default_applications" }
744
- );
745
- }
746
- };
747
-
748
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
749
- var import_md5 = __toESM(require("md5"), 1);
750
- function hashApplicationName(name) {
751
- if (!name) {
752
- throw new Error("Application name is required to generate hash");
753
- }
754
- return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
755
- }
756
-
757
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
758
- var PREFIX = "vc-ap";
759
- function generateAssetPrefixFromName({
760
- name
761
- }) {
762
- if (!name) {
763
- throw new Error("Name is required to generate an asset prefix");
764
- }
765
- return `${PREFIX}-${hashApplicationName(name)}`;
766
- }
767
-
768
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
769
- function generatePortFromName({
770
- name,
771
- minPort = 3e3,
772
- maxPort = 8e3
773
- }) {
774
- if (!name) {
775
- throw new Error("Name is required to generate a port");
776
- }
777
- let hash = 0;
778
- for (let i = 0; i < name.length; i++) {
779
- hash = (hash << 5) - hash + name.charCodeAt(i);
780
- hash |= 0;
781
- }
782
- hash = Math.abs(hash);
783
- const range = maxPort - minPort;
784
- const port = minPort + hash % range;
785
- return port;
786
- }
787
-
788
- // src/config/microfrontends-config/isomorphic/host.ts
789
- var Host = class {
790
- constructor(hostConfig, options) {
791
- if (typeof hostConfig === "string") {
792
- ({
793
- protocol: this.protocol,
794
- host: this.host,
795
- port: this.port
796
- } = Host.parseUrl(hostConfig));
797
- } else {
798
- const { protocol = "https", host, port } = hostConfig;
799
- this.protocol = protocol;
800
- this.host = host;
801
- this.port = port;
802
- }
803
- this.local = options?.isLocal;
804
- }
805
- static parseUrl(url, defaultProtocol = "https") {
806
- let hostToParse = url;
807
- if (!/^https?:\/\//.exec(hostToParse)) {
808
- hostToParse = `${defaultProtocol}://${hostToParse}`;
809
- }
810
- const parsed = new URL(hostToParse);
811
- if (!parsed.hostname) {
812
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
813
- }
814
- if (parsed.hash) {
815
- throw new Error(
816
- Host.getMicrofrontendsError(url, "cannot have a fragment")
817
- );
818
- }
819
- if (parsed.username || parsed.password) {
820
- throw new Error(
821
- Host.getMicrofrontendsError(
822
- url,
823
- "cannot have authentication credentials (username and/or password)"
824
- )
825
- );
826
- }
827
- if (parsed.pathname !== "/") {
828
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
829
- }
830
- if (parsed.search) {
831
- throw new Error(
832
- Host.getMicrofrontendsError(url, "cannot have query parameters")
833
- );
834
- }
835
- const protocol = parsed.protocol.slice(0, -1);
836
- return {
837
- protocol,
838
- host: parsed.hostname,
839
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
840
- };
841
- }
842
- static getMicrofrontendsError(url, message) {
843
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
844
- }
845
- isLocal() {
846
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
847
- }
848
- toString() {
849
- const url = this.toUrl();
850
- return url.toString().replace(/\/$/, "");
851
- }
852
- toUrl() {
853
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
854
- return new URL(url);
855
- }
856
- };
857
- var LocalHost = class extends Host {
858
- constructor({
859
- appName,
860
- local
861
- }) {
862
- let protocol;
863
- let host;
864
- let port;
865
- if (typeof local === "number") {
866
- port = local;
867
- } else if (typeof local === "string") {
868
- if (/^\d+$/.test(local)) {
869
- port = Number.parseInt(local);
870
- } else {
871
- const parsed = Host.parseUrl(local, "http");
872
- protocol = parsed.protocol;
873
- host = parsed.host;
874
- port = parsed.port;
875
- }
876
- } else if (local) {
877
- protocol = local.protocol;
878
- host = local.host;
879
- port = local.port;
880
- }
881
- super({
882
- protocol: protocol ?? "http",
883
- host: host ?? "localhost",
884
- port: port ?? generatePortFromName({ name: appName })
885
- });
886
- }
639
+ const applicationsWithoutRouting = Object.entries(
640
+ applicationConfigsById
641
+ ).filter(([, app]) => isDefaultApp(app));
642
+ const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
643
+ (acc) => {
644
+ return acc + 1;
645
+ },
646
+ 0
647
+ );
648
+ if (numApplicationsWithoutRouting === 0) {
649
+ throw new MicrofrontendError(
650
+ "No default application found. At least one application needs to be the default by omitting routing.",
651
+ { type: "config", subtype: "no_default_application" }
652
+ );
653
+ }
654
+ if (numApplicationsWithoutRouting > 1) {
655
+ const applicationNamesMissingRouting = applicationsWithoutRouting.map(
656
+ ([name]) => name
657
+ );
658
+ throw new MicrofrontendError(
659
+ `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
660
+ { type: "config", subtype: "multiple_default_applications" }
661
+ );
662
+ }
887
663
  };
888
664
 
889
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
890
- function generateAutomationBypassEnvVarName({
891
- name
892
- }) {
893
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
894
- }
895
-
896
665
  // src/config/microfrontends-config/isomorphic/application.ts
897
666
  var Application = class {
898
667
  constructor(name, {
@@ -973,9 +742,6 @@ var ChildApplication = class extends Application {
973
742
  }
974
743
  };
975
744
 
976
- // src/config/microfrontends-config/isomorphic/constants.ts
977
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
978
-
979
745
  // src/config/microfrontends-config/isomorphic/index.ts
980
746
  var MicrofrontendConfigIsomorphic = class {
981
747
  constructor({
@@ -1019,7 +785,7 @@ var MicrofrontendConfigIsomorphic = class {
1019
785
  };
1020
786
  }
1021
787
  static validate(config) {
1022
- 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;
1023
789
  validateConfigPaths(c.applications);
1024
790
  validateConfigDefaultApplication(c.applications);
1025
791
  return c;
@@ -1028,7 +794,7 @@ var MicrofrontendConfigIsomorphic = class {
1028
794
  cookies
1029
795
  }) {
1030
796
  return new MicrofrontendConfigIsomorphic({
1031
- config: (0, import_jsonc_parser2.parse)(getConfigStringFromEnv()),
797
+ config: (0, import_jsonc_parser.parse)(getConfigStringFromEnv()),
1032
798
  overrides: parseOverrides(cookies ?? [])
1033
799
  });
1034
800
  }
@@ -1094,9 +860,17 @@ var MicrofrontendConfigIsomorphic = class {
1094
860
  return this.defaultApplication;
1095
861
  }
1096
862
  /**
1097
- * 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.
1098
865
  */
1099
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
+ }
1100
874
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1101
875
  }
1102
876
  toClientConfig(options) {
@@ -1119,80 +893,394 @@ var MicrofrontendConfigIsomorphic = class {
1119
893
  {
1120
894
  removeFlaggedPaths: options?.removeFlaggedPaths
1121
895
  }
1122
- );
1123
- }
1124
- /**
1125
- * Serializes the class back to the Schema type.
1126
- *
1127
- * NOTE: This is used when writing the config to disk and must always match the input Schema
1128
- */
1129
- toSchemaJson() {
1130
- return this.serialized.config;
1131
- }
1132
- serialize() {
1133
- return this.serialized;
1134
- }
1135
- };
896
+ );
897
+ }
898
+ /**
899
+ * Serializes the class back to the Schema type.
900
+ *
901
+ * NOTE: This is used when writing the config to disk and must always match the input Schema
902
+ */
903
+ toSchemaJson() {
904
+ return this.serialized.config;
905
+ }
906
+ serialize() {
907
+ return this.serialized;
908
+ }
909
+ };
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
+
1007
+ // src/config/microfrontends/utils/get-application-context.ts
1008
+ var import_node_fs4 = __toESM(require("fs"), 1);
1009
+ var import_node_path4 = __toESM(require("path"), 1);
1010
+ function getApplicationContext(opts) {
1011
+ if (opts?.appName) {
1012
+ logger.debug(
1013
+ "[MFE Config] Application name from appName parameter:",
1014
+ opts.appName
1015
+ );
1016
+ return { name: opts.appName };
1017
+ }
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
+ );
1023
+ return {
1024
+ name: process.env.VERCEL_PROJECT_NAME,
1025
+ projectName: process.env.VERCEL_PROJECT_NAME
1026
+ };
1027
+ }
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
+ );
1033
+ return {
1034
+ name: process.env.NX_TASK_TARGET_PROJECT,
1035
+ packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1036
+ };
1037
+ }
1038
+ try {
1039
+ const vercelProjectJsonPath = import_node_fs4.default.readFileSync(
1040
+ import_node_path4.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1041
+ "utf-8"
1042
+ );
1043
+ const projectJson = JSON.parse(vercelProjectJsonPath);
1044
+ if (projectJson.projectName) {
1045
+ logger.debug(
1046
+ "[MFE Config] Application name from .vercel/project.json:",
1047
+ projectJson.projectName
1048
+ );
1049
+ return {
1050
+ name: projectJson.projectName,
1051
+ projectName: projectJson.projectName
1052
+ };
1053
+ }
1054
+ } catch (_) {
1055
+ }
1056
+ try {
1057
+ const packageJsonString = import_node_fs4.default.readFileSync(
1058
+ import_node_path4.default.join(opts?.packageRoot || ".", "package.json"),
1059
+ "utf-8"
1060
+ );
1061
+ const packageJson = JSON.parse(packageJsonString);
1062
+ if (!packageJson.name) {
1063
+ throw new MicrofrontendError(
1064
+ `package.json file missing required field "name"`,
1065
+ {
1066
+ type: "packageJson",
1067
+ subtype: "missing_field_name",
1068
+ source: "@vercel/microfrontends/next"
1069
+ }
1070
+ );
1071
+ }
1072
+ logger.debug(
1073
+ "[MFE Config] Application name from package.json:",
1074
+ packageJson.name
1075
+ );
1076
+ return { name: packageJson.name, packageJsonName: packageJson.name };
1077
+ } catch (err) {
1078
+ throw MicrofrontendError.handle(err, {
1079
+ fileName: "package.json"
1080
+ });
1081
+ }
1082
+ }
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}
1136
1176
 
1137
- // src/config/microfrontends/utils/get-application-context.ts
1138
- var import_node_fs6 = __toESM(require("fs"), 1);
1139
- var import_node_path6 = __toESM(require("path"), 1);
1140
- function getApplicationContext(opts) {
1141
- if (opts?.appName) {
1142
- logger.debug("[MFE Config] Application name from appName parameter:", opts.appName);
1143
- return { name: opts.appName };
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;
1144
1192
  }
1145
- if (process.env.VERCEL_PROJECT_NAME) {
1146
- logger.debug("[MFE Config] Application name from VERCEL_PROJECT_NAME:", process.env.VERCEL_PROJECT_NAME);
1147
- return {
1148
- name: process.env.VERCEL_PROJECT_NAME,
1149
- projectName: process.env.VERCEL_PROJECT_NAME
1150
- };
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];
1151
1198
  }
1152
- if (process.env.NX_TASK_TARGET_PROJECT) {
1153
- logger.debug("[MFE Config] Application name from NX_TASK_TARGET_PROJECT:", process.env.NX_TASK_TARGET_PROJECT);
1154
- return {
1155
- name: process.env.NX_TASK_TARGET_PROJECT,
1156
- packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1157
- };
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
+ );
1158
1205
  }
1206
+ configCache[cacheKey] = result;
1207
+ return result;
1208
+ }
1209
+ function existsSync(path6) {
1159
1210
  try {
1160
- const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1161
- import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1162
- "utf-8"
1163
- );
1164
- const projectJson = JSON.parse(vercelProjectJsonPath);
1165
- if (projectJson.projectName) {
1166
- logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
1167
- return {
1168
- name: projectJson.projectName,
1169
- projectName: projectJson.projectName
1170
- };
1171
- }
1211
+ (0, import_node_fs5.statSync)(path6);
1212
+ return true;
1172
1213
  } catch (_) {
1214
+ return false;
1173
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) {
1174
1229
  try {
1175
- const packageJsonString = import_node_fs6.default.readFileSync(
1176
- import_node_path6.default.join(opts?.packageRoot || ".", "package.json"),
1230
+ const microfrontendsJsonContent = (0, import_node_fs5.readFileSync)(
1231
+ microfrontendsJsonPath,
1177
1232
  "utf-8"
1178
1233
  );
1179
- const packageJson = JSON.parse(packageJsonString);
1180
- if (!packageJson.name) {
1181
- throw new MicrofrontendError(
1182
- `package.json file missing required field "name"`,
1183
- {
1184
- type: "packageJson",
1185
- subtype: "missing_field_name",
1186
- source: "@vercel/microfrontends/next"
1187
- }
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
1188
1239
  );
1240
+ return true;
1189
1241
  }
1190
- logger.debug("[MFE Config] Application name from package.json:", packageJson.name);
1191
- return { name: packageJson.name, packageJsonName: packageJson.name };
1192
- } catch (err) {
1193
- throw MicrofrontendError.handle(err, {
1194
- fileName: "package.json"
1195
- });
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;
1196
1284
  }
1197
1285
  }
1198
1286
 
@@ -1209,8 +1297,8 @@ function getOutputFilePath() {
1209
1297
  }
1210
1298
 
1211
1299
  // src/config/microfrontends/server/validation.ts
1212
- var import_jsonc_parser3 = require("jsonc-parser");
1213
1300
  var import_ajv = require("ajv");
1301
+ var import_jsonc_parser3 = require("jsonc-parser");
1214
1302
 
1215
1303
  // schema/schema.json
1216
1304
  var schema_default = {
@@ -1238,9 +1326,7 @@ var schema_default = {
1238
1326
  description: "Optional configuration options for the microfrontend."
1239
1327
  }
1240
1328
  },
1241
- required: [
1242
- "applications"
1243
- ],
1329
+ required: ["applications"],
1244
1330
  additionalProperties: false,
1245
1331
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1246
1332
  },
@@ -1277,19 +1363,14 @@ var schema_default = {
1277
1363
  description: "Development configuration for the default application."
1278
1364
  }
1279
1365
  },
1280
- required: [
1281
- "development"
1282
- ],
1366
+ required: ["development"],
1283
1367
  additionalProperties: false
1284
1368
  },
1285
1369
  DefaultDevelopment: {
1286
1370
  type: "object",
1287
1371
  properties: {
1288
1372
  local: {
1289
- type: [
1290
- "number",
1291
- "string"
1292
- ],
1373
+ type: ["number", "string"],
1293
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."
1294
1375
  },
1295
1376
  task: {
@@ -1301,9 +1382,7 @@ var schema_default = {
1301
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."
1302
1383
  }
1303
1384
  },
1304
- required: [
1305
- "fallback"
1306
- ],
1385
+ required: ["fallback"],
1307
1386
  additionalProperties: false
1308
1387
  },
1309
1388
  ChildApplication: {
@@ -1326,19 +1405,14 @@ var schema_default = {
1326
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."
1327
1406
  }
1328
1407
  },
1329
- required: [
1330
- "routing"
1331
- ],
1408
+ required: ["routing"],
1332
1409
  additionalProperties: false
1333
1410
  },
1334
1411
  ChildDevelopment: {
1335
1412
  type: "object",
1336
1413
  properties: {
1337
1414
  local: {
1338
- type: [
1339
- "number",
1340
- "string"
1341
- ],
1415
+ type: ["number", "string"],
1342
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."
1343
1417
  },
1344
1418
  task: {
@@ -1378,9 +1452,7 @@ var schema_default = {
1378
1452
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1379
1453
  }
1380
1454
  },
1381
- required: [
1382
- "paths"
1383
- ],
1455
+ required: ["paths"],
1384
1456
  additionalProperties: false,
1385
1457
  description: "A group of paths that is routed to this application."
1386
1458
  },
@@ -1742,6 +1814,51 @@ var MicrofrontendsServer = class {
1742
1814
  }
1743
1815
  };
1744
1816
 
1817
+ // src/next/config/env.ts
1818
+ function debugEnv(env) {
1819
+ const indent = " ".repeat(4);
1820
+ const header = "env (key \u2192 val)";
1821
+ const separator = "\u23AF".repeat(header.length);
1822
+ const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
1823
+ const table = Object.keys(env).map((key, idx) => {
1824
+ const paddedKey = key.padEnd(maxKeyLength);
1825
+ return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
1826
+ }).join("\n");
1827
+ logger.debug(`${indent}${header}
1828
+ ${indent}${separator}
1829
+ ${table}
1830
+ `);
1831
+ }
1832
+ function setEnvironment({
1833
+ app,
1834
+ microfrontends
1835
+ }) {
1836
+ const clientEnvs = {
1837
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1838
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
1839
+ NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
1840
+ microfrontends.config.toClientConfig({
1841
+ removeFlaggedPaths: true
1842
+ }).serialize()
1843
+ ),
1844
+ ...app.getAssetPrefix() ? {
1845
+ NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
1846
+ } : {},
1847
+ ...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
1848
+ NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
1849
+ } : {}
1850
+ };
1851
+ const serverEnvs = {
1852
+ MFE_CURRENT_APPLICATION: app.name,
1853
+ MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1854
+ };
1855
+ const allEnvs = { ...clientEnvs, ...serverEnvs };
1856
+ for (const [key, value] of Object.entries(allEnvs)) {
1857
+ process.env[key] = value;
1858
+ }
1859
+ debugEnv(allEnvs);
1860
+ }
1861
+
1745
1862
  // src/next/config/transforms/asset-prefix.ts
1746
1863
  function transform(args) {
1747
1864
  const { next, app } = args;
@@ -1846,20 +1963,6 @@ ${indent} - Automatically redirecting all requests to local microfrontends proxy
1846
1963
  return { next };
1847
1964
  }
1848
1965
 
1849
- // src/next/config/transforms/transpile-packages.ts
1850
- function transform5(args) {
1851
- const { next } = args;
1852
- if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
1853
- next.transpilePackages = [
1854
- ...next.transpilePackages || [],
1855
- "@vercel/microfrontends"
1856
- ];
1857
- }
1858
- return {
1859
- next
1860
- };
1861
- }
1862
-
1863
1966
  // src/next/config/transforms/rewrites.ts
1864
1967
  function debugRewrites(rewrites) {
1865
1968
  const indent = " ".repeat(4);
@@ -1888,7 +1991,7 @@ function rewritesMapToArr(rewrites) {
1888
1991
  ];
1889
1992
  });
1890
1993
  }
1891
- function transform6(args) {
1994
+ function transform5(args) {
1892
1995
  const { app, next } = args;
1893
1996
  const buildBeforeFiles = () => {
1894
1997
  const rewrites = /* @__PURE__ */ new Map();
@@ -1951,6 +2054,20 @@ function transform6(args) {
1951
2054
  };
1952
2055
  }
1953
2056
 
2057
+ // src/next/config/transforms/transpile-packages.ts
2058
+ function transform6(args) {
2059
+ const { next } = args;
2060
+ if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
2061
+ next.transpilePackages = [
2062
+ ...next.transpilePackages || [],
2063
+ "@vercel/microfrontends"
2064
+ ];
2065
+ }
2066
+ return {
2067
+ next
2068
+ };
2069
+ }
2070
+
1954
2071
  // src/next/config/transforms/webpack.ts
1955
2072
  var import_node_fs8 = __toESM(require("fs"), 1);
1956
2073
  var import_node_module = require("module");
@@ -1984,8 +2101,12 @@ var nextVersion = getNextJsVersion();
1984
2101
  function transform7(args) {
1985
2102
  const useDefineServer = args.opts?.preferWebpackEnvironmentPlugin ? false : semver.gte(nextVersion, "15.4.0-canary.41");
1986
2103
  const { next, microfrontend, opts } = args;
2104
+ const isNext16OrHigher = semver.gte(nextVersion, "16.0.0");
2105
+ const hasTurbopackConfig = Boolean(next.turbopack);
2106
+ const turbopackConfig = isNext16OrHigher && !hasTurbopackConfig ? { turbopack: {} } : {};
1987
2107
  const configWithWebpack = {
1988
2108
  ...next,
2109
+ ...turbopackConfig,
1989
2110
  ...useDefineServer ? {
1990
2111
  compiler: {
1991
2112
  ...next.compiler,
@@ -2065,56 +2186,11 @@ var transforms = {
2065
2186
  buildId: transform2,
2066
2187
  draftMode: transform3,
2067
2188
  redirects: transform4,
2068
- rewrites: transform6,
2069
- transpilePackages: transform5,
2189
+ rewrites: transform5,
2190
+ transpilePackages: transform6,
2070
2191
  webpack: transform7
2071
2192
  };
2072
2193
 
2073
- // src/next/config/env.ts
2074
- function debugEnv(env) {
2075
- const indent = " ".repeat(4);
2076
- const header = "env (key \u2192 val)";
2077
- const separator = "\u23AF".repeat(header.length);
2078
- const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
2079
- const table = Object.keys(env).map((key, idx) => {
2080
- const paddedKey = key.padEnd(maxKeyLength);
2081
- return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
2082
- }).join("\n");
2083
- logger.debug(`${indent}${header}
2084
- ${indent}${separator}
2085
- ${table}
2086
- `);
2087
- }
2088
- function setEnvironment({
2089
- app,
2090
- microfrontends
2091
- }) {
2092
- const clientEnvs = {
2093
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
2094
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
2095
- NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
2096
- microfrontends.config.toClientConfig({
2097
- removeFlaggedPaths: true
2098
- }).serialize()
2099
- ),
2100
- ...app.getAssetPrefix() ? {
2101
- NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
2102
- } : {},
2103
- ...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
2104
- NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
2105
- } : {}
2106
- };
2107
- const serverEnvs = {
2108
- MFE_CURRENT_APPLICATION: app.name,
2109
- MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
2110
- };
2111
- const allEnvs = { ...clientEnvs, ...serverEnvs };
2112
- for (const [key, value] of Object.entries(allEnvs)) {
2113
- process.env[key] = value;
2114
- }
2115
- debugEnv(allEnvs);
2116
- }
2117
-
2118
2194
  // src/next/config/index.ts
2119
2195
  function typedEntries(obj) {
2120
2196
  return Object.entries(obj);