@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
@@ -37,38 +37,27 @@ module.exports = __toCommonJS(server_exports);
37
37
  var import_node_fs7 = __toESM(require("fs"), 1);
38
38
  var import_node_path8 = require("path");
39
39
 
40
- // src/config/overrides/constants.ts
41
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
42
- var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
43
-
44
- // src/config/overrides/is-override-cookie.ts
45
- function isOverrideCookie(cookie) {
46
- return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
40
+ // src/bin/logger.ts
41
+ function debug(...args) {
42
+ if (process.env.MFE_DEBUG) {
43
+ console.log(...args);
44
+ }
47
45
  }
48
-
49
- // src/config/overrides/get-override-from-cookie.ts
50
- function getOverrideFromCookie(cookie) {
51
- if (!isOverrideCookie(cookie) || !cookie.value)
52
- return;
53
- return {
54
- application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
55
- host: cookie.value
56
- };
46
+ function info(...args) {
47
+ console.log(...args);
57
48
  }
58
-
59
- // src/config/overrides/parse-overrides.ts
60
- function parseOverrides(cookies) {
61
- const overridesConfig = { applications: {} };
62
- cookies.forEach((cookie) => {
63
- const override = getOverrideFromCookie(cookie);
64
- if (!override)
65
- return;
66
- overridesConfig.applications[override.application] = {
67
- environment: { host: override.host }
68
- };
69
- });
70
- return overridesConfig;
49
+ function warn(...args) {
50
+ console.warn(...args);
71
51
  }
52
+ function error(...args) {
53
+ console.error(...args);
54
+ }
55
+ var logger = {
56
+ debug,
57
+ info,
58
+ warn,
59
+ error
60
+ };
72
61
 
73
62
  // src/config/errors.ts
74
63
  var MicrofrontendError = class extends Error {
@@ -162,296 +151,47 @@ var MicrofrontendError = class extends Error {
162
151
  }
163
152
  };
164
153
 
165
- // src/config/microfrontends-config/utils/get-config-from-env.ts
166
- function getConfigStringFromEnv() {
167
- const config = process.env.MFE_CONFIG;
168
- if (!config) {
169
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
170
- type: "config",
171
- subtype: "not_found_in_env"
172
- });
173
- }
174
- return config;
175
- }
176
-
177
- // src/config/schema/utils/is-default-app.ts
178
- function isDefaultApp(a) {
179
- return !("routing" in a);
180
- }
181
-
182
- // src/config/microfrontends/utils/find-repository-root.ts
183
- var import_node_fs = __toESM(require("fs"), 1);
184
- var import_node_path = __toESM(require("path"), 1);
185
- var GIT_DIRECTORY = ".git";
186
- function hasGitDirectory(dir) {
187
- const gitPath = import_node_path.default.join(dir, GIT_DIRECTORY);
188
- return import_node_fs.default.existsSync(gitPath) && import_node_fs.default.statSync(gitPath).isDirectory();
189
- }
190
- function hasPnpmWorkspaces(dir) {
191
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "pnpm-workspace.yaml"));
192
- }
193
- function hasPackageJson(dir) {
194
- return import_node_fs.default.existsSync(import_node_path.default.join(dir, "package.json"));
195
- }
196
- function findRepositoryRoot(startDir) {
197
- if (process.env.NX_WORKSPACE_ROOT) {
198
- return process.env.NX_WORKSPACE_ROOT;
199
- }
200
- let currentDir = startDir || process.cwd();
201
- let lastPackageJsonDir = null;
202
- while (currentDir !== import_node_path.default.parse(currentDir).root) {
203
- if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
204
- return currentDir;
205
- }
206
- if (hasPackageJson(currentDir)) {
207
- lastPackageJsonDir = currentDir;
208
- }
209
- currentDir = import_node_path.default.dirname(currentDir);
210
- }
211
- if (lastPackageJsonDir) {
212
- return lastPackageJsonDir;
213
- }
214
- throw new Error(
215
- `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.`
216
- );
217
- }
218
-
219
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
220
- var import_node_path2 = require("path");
221
- var import_node_fs2 = require("fs");
154
+ // src/config/microfrontends-config/isomorphic/index.ts
222
155
  var import_jsonc_parser = require("jsonc-parser");
223
- var import_fast_glob = __toESM(require("fast-glob"), 1);
224
-
225
- // src/bin/logger.ts
226
- function debug(...args) {
227
- if (process.env.MFE_DEBUG) {
228
- console.log(...args);
229
- }
230
- }
231
- function info(...args) {
232
- console.log(...args);
233
- }
234
- function warn(...args) {
235
- console.warn(...args);
236
- }
237
- function error(...args) {
238
- console.error(...args);
239
- }
240
- var logger = {
241
- debug,
242
- info,
243
- warn,
244
- error
245
- };
246
156
 
247
- // src/config/microfrontends/utils/get-config-file-name.ts
248
- var DEFAULT_CONFIGURATION_FILENAMES = [
249
- "microfrontends.json",
250
- "microfrontends.jsonc"
251
- ];
252
- function getPossibleConfigurationFilenames({
253
- customConfigFilename
254
- }) {
255
- if (customConfigFilename) {
256
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
257
- throw new Error(
258
- `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.`
259
- );
260
- }
261
- return Array.from(
262
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
263
- );
264
- }
265
- return DEFAULT_CONFIGURATION_FILENAMES;
266
- }
267
-
268
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
269
- var configCache = {};
270
- function findPackageWithMicrofrontendsConfig({
271
- repositoryRoot,
272
- applicationContext,
273
- customConfigFilename
274
- }) {
275
- const applicationName = applicationContext.name;
276
- logger.debug(
277
- "[MFE Config] Searching repository for configs containing application:",
278
- applicationName
279
- );
280
- try {
281
- const microfrontendsJsonPaths = import_fast_glob.default.globSync(
282
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
283
- {
284
- cwd: repositoryRoot,
285
- absolute: true,
286
- onlyFiles: true,
287
- followSymbolicLinks: false,
288
- ignore: ["**/node_modules/**", "**/.git/**"]
289
- }
290
- );
291
- logger.debug(
292
- "[MFE Config] Found",
293
- microfrontendsJsonPaths.length,
294
- "config file(s) in repository"
295
- );
296
- const matchingPaths = [];
297
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
298
- try {
299
- const microfrontendsJsonContent = (0, import_node_fs2.readFileSync)(
300
- microfrontendsJsonPath,
301
- "utf-8"
302
- );
303
- const microfrontendsJson = (0, import_jsonc_parser.parse)(microfrontendsJsonContent);
304
- if (microfrontendsJson.applications[applicationName]) {
305
- logger.debug(
306
- "[MFE Config] Found application in config:",
307
- microfrontendsJsonPath
308
- );
309
- matchingPaths.push(microfrontendsJsonPath);
310
- } else {
311
- for (const [_, app] of Object.entries(
312
- microfrontendsJson.applications
313
- )) {
314
- if (app.packageName === applicationName) {
315
- logger.debug(
316
- "[MFE Config] Found application via packageName in config:",
317
- microfrontendsJsonPath
318
- );
319
- matchingPaths.push(microfrontendsJsonPath);
320
- }
321
- }
322
- }
323
- } catch (error2) {
324
- }
325
- }
326
- logger.debug(
327
- "[MFE Config] Total matching config files:",
328
- matchingPaths.length
329
- );
330
- if (matchingPaths.length > 1) {
331
- throw new MicrofrontendError(
332
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
333
- ${matchingPaths.join("\n \u2022 ")}`,
334
- { type: "config", subtype: "inference_failed" }
335
- );
336
- }
337
- if (matchingPaths.length === 0) {
338
- let additionalErrorMessage = "";
339
- if (microfrontendsJsonPaths.length > 0) {
340
- if (!applicationContext.projectName) {
341
- additionalErrorMessage = `
342
-
343
- 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.`;
344
- } else {
345
- additionalErrorMessage = `
346
-
347
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
348
- }
349
- }
350
- throw new MicrofrontendError(
351
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
352
-
353
- 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.
354
-
355
- 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.
157
+ // src/config/overrides/constants.ts
158
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
159
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
356
160
 
357
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
358
- { type: "config", subtype: "inference_failed" }
359
- );
360
- }
361
- const [packageJsonPath] = matchingPaths;
362
- return (0, import_node_path2.dirname)(packageJsonPath);
363
- } catch (error2) {
364
- if (error2 instanceof MicrofrontendError) {
365
- throw error2;
366
- }
367
- return null;
368
- }
369
- }
370
- function inferMicrofrontendsLocation(opts) {
371
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
372
- if (configCache[cacheKey]) {
373
- return configCache[cacheKey];
374
- }
375
- const result = findPackageWithMicrofrontendsConfig(opts);
376
- if (!result) {
377
- throw new MicrofrontendError(
378
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
379
- { type: "config", subtype: "inference_failed" }
380
- );
381
- }
382
- configCache[cacheKey] = result;
383
- return result;
161
+ // src/config/overrides/is-override-cookie.ts
162
+ function isOverrideCookie(cookie) {
163
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
384
164
  }
385
165
 
386
- // src/config/microfrontends/utils/is-monorepo.ts
387
- var import_node_fs3 = __toESM(require("fs"), 1);
388
- var import_node_path3 = __toESM(require("path"), 1);
389
- function isMonorepo({
390
- repositoryRoot
391
- }) {
392
- try {
393
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
394
- return true;
395
- }
396
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(repositoryRoot, "vlt-workspaces.json"))) {
397
- return true;
398
- }
399
- if (process.env.NX_WORKSPACE_ROOT === import_node_path3.default.resolve(repositoryRoot)) {
400
- return true;
401
- }
402
- const packageJsonPath = import_node_path3.default.join(repositoryRoot, "package.json");
403
- if (!import_node_fs3.default.existsSync(packageJsonPath)) {
404
- return false;
405
- }
406
- const packageJson = JSON.parse(
407
- import_node_fs3.default.readFileSync(packageJsonPath, "utf-8")
408
- );
409
- return packageJson.workspaces !== void 0;
410
- } catch (error2) {
411
- logger.error("Error determining if repository is a monorepo", error2);
412
- return false;
413
- }
166
+ // src/config/overrides/get-override-from-cookie.ts
167
+ function getOverrideFromCookie(cookie) {
168
+ if (!isOverrideCookie(cookie) || !cookie.value)
169
+ return;
170
+ return {
171
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
172
+ host: cookie.value
173
+ };
414
174
  }
415
175
 
416
- // src/config/microfrontends/utils/find-package-root.ts
417
- var import_node_fs4 = __toESM(require("fs"), 1);
418
- var import_node_path4 = __toESM(require("path"), 1);
419
- var PACKAGE_JSON = "package.json";
420
- function findPackageRoot(startDir) {
421
- let currentDir = startDir || process.cwd();
422
- while (currentDir !== import_node_path4.default.parse(currentDir).root) {
423
- const pkgJsonPath = import_node_path4.default.join(currentDir, PACKAGE_JSON);
424
- if (import_node_fs4.default.existsSync(pkgJsonPath)) {
425
- return currentDir;
426
- }
427
- currentDir = import_node_path4.default.dirname(currentDir);
428
- }
429
- throw new Error(
430
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
431
- );
176
+ // src/config/overrides/parse-overrides.ts
177
+ function parseOverrides(cookies) {
178
+ const overridesConfig = { applications: {} };
179
+ cookies.forEach((cookie) => {
180
+ const override = getOverrideFromCookie(cookie);
181
+ if (!override)
182
+ return;
183
+ overridesConfig.applications[override.application] = {
184
+ environment: { host: override.host }
185
+ };
186
+ });
187
+ return overridesConfig;
432
188
  }
433
189
 
434
- // src/config/microfrontends/utils/find-config.ts
435
- var import_node_fs5 = __toESM(require("fs"), 1);
436
- var import_node_path5 = require("path");
437
- function findConfig({
438
- dir,
439
- customConfigFilename
440
- }) {
441
- for (const filename of getPossibleConfigurationFilenames({
442
- customConfigFilename
443
- })) {
444
- const maybeConfig = (0, import_node_path5.join)(dir, filename);
445
- if (import_node_fs5.default.existsSync(maybeConfig)) {
446
- return maybeConfig;
447
- }
448
- }
449
- return null;
190
+ // src/config/schema/utils/is-default-app.ts
191
+ function isDefaultApp(a) {
192
+ return !("routing" in a);
450
193
  }
451
194
 
452
- // src/config/microfrontends-config/isomorphic/index.ts
453
- var import_jsonc_parser2 = require("jsonc-parser");
454
-
455
195
  // src/config/microfrontends-config/client/index.ts
456
196
  var import_path_to_regexp = require("path-to-regexp");
457
197
  var regexpCache = /* @__PURE__ */ new Map();
@@ -510,46 +250,223 @@ var MicrofrontendConfigClient = class {
510
250
  "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"
511
251
  );
512
252
  }
513
- return new MicrofrontendConfigClient(JSON.parse(config));
253
+ return new MicrofrontendConfigClient(JSON.parse(config));
254
+ }
255
+ isEqual(other) {
256
+ return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
257
+ }
258
+ getApplicationNameForPath(path6) {
259
+ if (!path6.startsWith("/")) {
260
+ throw new Error(`Path must start with a /`);
261
+ }
262
+ if (this.pathCache[path6]) {
263
+ return this.pathCache[path6];
264
+ }
265
+ const pathname = new URL(path6, "https://example.com").pathname;
266
+ for (const [name, application] of Object.entries(this.applications)) {
267
+ if (application.routing) {
268
+ for (const group of application.routing) {
269
+ for (const childPath of group.paths) {
270
+ const regexp = getRegexp(childPath);
271
+ if (regexp.test(pathname)) {
272
+ this.pathCache[path6] = name;
273
+ return name;
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ const defaultApplication = Object.entries(this.applications).find(
280
+ ([, application]) => application.default
281
+ );
282
+ if (!defaultApplication) {
283
+ return null;
284
+ }
285
+ this.pathCache[path6] = defaultApplication[0];
286
+ return defaultApplication[0];
287
+ }
288
+ serialize() {
289
+ return this.serialized;
290
+ }
291
+ };
292
+
293
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
294
+ function getConfigStringFromEnv() {
295
+ const config = process.env.MFE_CONFIG;
296
+ if (!config) {
297
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
298
+ type: "config",
299
+ subtype: "not_found_in_env"
300
+ });
301
+ }
302
+ return config;
303
+ }
304
+
305
+ // src/config/microfrontends-config/isomorphic/constants.ts
306
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
307
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
308
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
309
+
310
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
311
+ function generatePortFromName({
312
+ name,
313
+ minPort = 3e3,
314
+ maxPort = 8e3
315
+ }) {
316
+ if (!name) {
317
+ throw new Error("Name is required to generate a port");
318
+ }
319
+ let hash = 0;
320
+ for (let i = 0; i < name.length; i++) {
321
+ hash = (hash << 5) - hash + name.charCodeAt(i);
322
+ hash |= 0;
323
+ }
324
+ hash = Math.abs(hash);
325
+ const range = maxPort - minPort;
326
+ const port = minPort + hash % range;
327
+ return port;
328
+ }
329
+
330
+ // src/config/microfrontends-config/isomorphic/host.ts
331
+ var Host = class {
332
+ constructor(hostConfig, options) {
333
+ if (typeof hostConfig === "string") {
334
+ ({
335
+ protocol: this.protocol,
336
+ host: this.host,
337
+ port: this.port
338
+ } = Host.parseUrl(hostConfig));
339
+ } else {
340
+ const { protocol = "https", host, port } = hostConfig;
341
+ this.protocol = protocol;
342
+ this.host = host;
343
+ this.port = port;
344
+ }
345
+ this.local = options?.isLocal;
346
+ }
347
+ static parseUrl(url, defaultProtocol = "https") {
348
+ let hostToParse = url;
349
+ if (!/^https?:\/\//.exec(hostToParse)) {
350
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
351
+ }
352
+ const parsed = new URL(hostToParse);
353
+ if (!parsed.hostname) {
354
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
355
+ }
356
+ if (parsed.hash) {
357
+ throw new Error(
358
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
359
+ );
360
+ }
361
+ if (parsed.username || parsed.password) {
362
+ throw new Error(
363
+ Host.getMicrofrontendsError(
364
+ url,
365
+ "cannot have authentication credentials (username and/or password)"
366
+ )
367
+ );
368
+ }
369
+ if (parsed.pathname !== "/") {
370
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
371
+ }
372
+ if (parsed.search) {
373
+ throw new Error(
374
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
375
+ );
376
+ }
377
+ const protocol = parsed.protocol.slice(0, -1);
378
+ return {
379
+ protocol,
380
+ host: parsed.hostname,
381
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
382
+ };
514
383
  }
515
- isEqual(other) {
516
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
384
+ static getMicrofrontendsError(url, message) {
385
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
517
386
  }
518
- getApplicationNameForPath(path6) {
519
- if (!path6.startsWith("/")) {
520
- throw new Error(`Path must start with a /`);
521
- }
522
- if (this.pathCache[path6]) {
523
- return this.pathCache[path6];
524
- }
525
- const pathname = new URL(path6, "https://example.com").pathname;
526
- for (const [name, application] of Object.entries(this.applications)) {
527
- if (application.routing) {
528
- for (const group of application.routing) {
529
- for (const childPath of group.paths) {
530
- const regexp = getRegexp(childPath);
531
- if (regexp.test(pathname)) {
532
- this.pathCache[path6] = name;
533
- return name;
534
- }
535
- }
536
- }
387
+ isLocal() {
388
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
389
+ }
390
+ toString() {
391
+ const url = this.toUrl();
392
+ return url.toString().replace(/\/$/, "");
393
+ }
394
+ toUrl() {
395
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
396
+ return new URL(url);
397
+ }
398
+ };
399
+ var LocalHost = class extends Host {
400
+ constructor({
401
+ appName,
402
+ local
403
+ }) {
404
+ const portOverride = process.env[MFE_APP_PORT_ENV];
405
+ if (portOverride) {
406
+ const overridePort = Number.parseInt(portOverride, 10);
407
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
408
+ super({
409
+ protocol: "http",
410
+ host: "localhost",
411
+ port: overridePort
412
+ });
413
+ return;
537
414
  }
538
415
  }
539
- const defaultApplication = Object.entries(this.applications).find(
540
- ([, application]) => application.default
541
- );
542
- if (!defaultApplication) {
543
- return null;
416
+ let protocol;
417
+ let host;
418
+ let port;
419
+ if (typeof local === "number") {
420
+ port = local;
421
+ } else if (typeof local === "string") {
422
+ if (/^\d+$/.test(local)) {
423
+ port = Number.parseInt(local, 10);
424
+ } else {
425
+ const parsed = Host.parseUrl(local, "http");
426
+ protocol = parsed.protocol;
427
+ host = parsed.host;
428
+ port = parsed.port;
429
+ }
430
+ } else if (local) {
431
+ protocol = local.protocol;
432
+ host = local.host;
433
+ port = local.port;
544
434
  }
545
- this.pathCache[path6] = defaultApplication[0];
546
- return defaultApplication[0];
547
- }
548
- serialize() {
549
- return this.serialized;
435
+ super({
436
+ protocol: protocol ?? "http",
437
+ host: host ?? "localhost",
438
+ port: port ?? generatePortFromName({ name: appName })
439
+ });
550
440
  }
551
441
  };
552
442
 
443
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
444
+ var import_md5 = __toESM(require("md5"), 1);
445
+ function hashApplicationName(name) {
446
+ if (!name) {
447
+ throw new Error("Application name is required to generate hash");
448
+ }
449
+ return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
450
+ }
451
+
452
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
453
+ var PREFIX = "vc-ap";
454
+ function generateAssetPrefixFromName({
455
+ name
456
+ }) {
457
+ if (!name) {
458
+ throw new Error("Name is required to generate an asset prefix");
459
+ }
460
+ return `${PREFIX}-${hashApplicationName(name)}`;
461
+ }
462
+
463
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
464
+ function generateAutomationBypassEnvVarName({
465
+ name
466
+ }) {
467
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
468
+ }
469
+
553
470
  // src/config/microfrontends-config/isomorphic/validation.ts
554
471
  var import_path_to_regexp2 = require("path-to-regexp");
555
472
  var LIST_FORMATTER = new Intl.ListFormat("en", {
@@ -731,154 +648,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
731
648
  }
732
649
  };
733
650
 
734
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
735
- var import_md5 = __toESM(require("md5"), 1);
736
- function hashApplicationName(name) {
737
- if (!name) {
738
- throw new Error("Application name is required to generate hash");
739
- }
740
- return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
741
- }
742
-
743
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
744
- var PREFIX = "vc-ap";
745
- function generateAssetPrefixFromName({
746
- name
747
- }) {
748
- if (!name) {
749
- throw new Error("Name is required to generate an asset prefix");
750
- }
751
- return `${PREFIX}-${hashApplicationName(name)}`;
752
- }
753
-
754
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
755
- function generatePortFromName({
756
- name,
757
- minPort = 3e3,
758
- maxPort = 8e3
759
- }) {
760
- if (!name) {
761
- throw new Error("Name is required to generate a port");
762
- }
763
- let hash = 0;
764
- for (let i = 0; i < name.length; i++) {
765
- hash = (hash << 5) - hash + name.charCodeAt(i);
766
- hash |= 0;
767
- }
768
- hash = Math.abs(hash);
769
- const range = maxPort - minPort;
770
- const port = minPort + hash % range;
771
- return port;
772
- }
773
-
774
- // src/config/microfrontends-config/isomorphic/host.ts
775
- var Host = class {
776
- constructor(hostConfig, options) {
777
- if (typeof hostConfig === "string") {
778
- ({
779
- protocol: this.protocol,
780
- host: this.host,
781
- port: this.port
782
- } = Host.parseUrl(hostConfig));
783
- } else {
784
- const { protocol = "https", host, port } = hostConfig;
785
- this.protocol = protocol;
786
- this.host = host;
787
- this.port = port;
788
- }
789
- this.local = options?.isLocal;
790
- }
791
- static parseUrl(url, defaultProtocol = "https") {
792
- let hostToParse = url;
793
- if (!/^https?:\/\//.exec(hostToParse)) {
794
- hostToParse = `${defaultProtocol}://${hostToParse}`;
795
- }
796
- const parsed = new URL(hostToParse);
797
- if (!parsed.hostname) {
798
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
799
- }
800
- if (parsed.hash) {
801
- throw new Error(
802
- Host.getMicrofrontendsError(url, "cannot have a fragment")
803
- );
804
- }
805
- if (parsed.username || parsed.password) {
806
- throw new Error(
807
- Host.getMicrofrontendsError(
808
- url,
809
- "cannot have authentication credentials (username and/or password)"
810
- )
811
- );
812
- }
813
- if (parsed.pathname !== "/") {
814
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
815
- }
816
- if (parsed.search) {
817
- throw new Error(
818
- Host.getMicrofrontendsError(url, "cannot have query parameters")
819
- );
820
- }
821
- const protocol = parsed.protocol.slice(0, -1);
822
- return {
823
- protocol,
824
- host: parsed.hostname,
825
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
826
- };
827
- }
828
- static getMicrofrontendsError(url, message) {
829
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
830
- }
831
- isLocal() {
832
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
833
- }
834
- toString() {
835
- const url = this.toUrl();
836
- return url.toString().replace(/\/$/, "");
837
- }
838
- toUrl() {
839
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
840
- return new URL(url);
841
- }
842
- };
843
- var LocalHost = class extends Host {
844
- constructor({
845
- appName,
846
- local
847
- }) {
848
- let protocol;
849
- let host;
850
- let port;
851
- if (typeof local === "number") {
852
- port = local;
853
- } else if (typeof local === "string") {
854
- if (/^\d+$/.test(local)) {
855
- port = Number.parseInt(local);
856
- } else {
857
- const parsed = Host.parseUrl(local, "http");
858
- protocol = parsed.protocol;
859
- host = parsed.host;
860
- port = parsed.port;
861
- }
862
- } else if (local) {
863
- protocol = local.protocol;
864
- host = local.host;
865
- port = local.port;
866
- }
867
- super({
868
- protocol: protocol ?? "http",
869
- host: host ?? "localhost",
870
- port: port ?? generatePortFromName({ name: appName })
871
- });
872
- }
873
- };
874
-
875
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
876
- function generateAutomationBypassEnvVarName({
877
- name
878
- }) {
879
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
880
- }
881
-
882
651
  // src/config/microfrontends-config/isomorphic/application.ts
883
652
  var Application = class {
884
653
  constructor(name, {
@@ -959,9 +728,6 @@ var ChildApplication = class extends Application {
959
728
  }
960
729
  };
961
730
 
962
- // src/config/microfrontends-config/isomorphic/constants.ts
963
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
964
-
965
731
  // src/config/microfrontends-config/isomorphic/index.ts
966
732
  var MicrofrontendConfigIsomorphic = class {
967
733
  constructor({
@@ -1005,7 +771,7 @@ var MicrofrontendConfigIsomorphic = class {
1005
771
  };
1006
772
  }
1007
773
  static validate(config) {
1008
- const c = typeof config === "string" ? (0, import_jsonc_parser2.parse)(config) : config;
774
+ const c = typeof config === "string" ? (0, import_jsonc_parser.parse)(config) : config;
1009
775
  validateConfigPaths(c.applications);
1010
776
  validateConfigDefaultApplication(c.applications);
1011
777
  return c;
@@ -1014,7 +780,7 @@ var MicrofrontendConfigIsomorphic = class {
1014
780
  cookies
1015
781
  }) {
1016
782
  return new MicrofrontendConfigIsomorphic({
1017
- config: (0, import_jsonc_parser2.parse)(getConfigStringFromEnv()),
783
+ config: (0, import_jsonc_parser.parse)(getConfigStringFromEnv()),
1018
784
  overrides: parseOverrides(cookies ?? [])
1019
785
  });
1020
786
  }
@@ -1080,9 +846,17 @@ var MicrofrontendConfigIsomorphic = class {
1080
846
  return this.defaultApplication;
1081
847
  }
1082
848
  /**
1083
- * Returns the configured port for the local proxy
849
+ * Returns the configured port for the local proxy.
850
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
1084
851
  */
1085
852
  getLocalProxyPort() {
853
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
854
+ if (portOverride) {
855
+ const port = Number.parseInt(portOverride, 10);
856
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
857
+ return port;
858
+ }
859
+ }
1086
860
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1087
861
  }
1088
862
  toClientConfig(options) {
@@ -1120,36 +894,144 @@ var MicrofrontendConfigIsomorphic = class {
1120
894
  }
1121
895
  };
1122
896
 
897
+ // src/config/microfrontends/utils/find-config.ts
898
+ var import_node_fs = __toESM(require("fs"), 1);
899
+ var import_node_path = require("path");
900
+
901
+ // src/config/microfrontends/utils/get-config-file-name.ts
902
+ var DEFAULT_CONFIGURATION_FILENAMES = [
903
+ "microfrontends.json",
904
+ "microfrontends.jsonc"
905
+ ];
906
+ function getPossibleConfigurationFilenames({
907
+ customConfigFilename
908
+ }) {
909
+ if (customConfigFilename) {
910
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
911
+ throw new Error(
912
+ `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.`
913
+ );
914
+ }
915
+ return Array.from(
916
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
917
+ );
918
+ }
919
+ return DEFAULT_CONFIGURATION_FILENAMES;
920
+ }
921
+
922
+ // src/config/microfrontends/utils/find-config.ts
923
+ function findConfig({
924
+ dir,
925
+ customConfigFilename
926
+ }) {
927
+ for (const filename of getPossibleConfigurationFilenames({
928
+ customConfigFilename
929
+ })) {
930
+ const maybeConfig = (0, import_node_path.join)(dir, filename);
931
+ if (import_node_fs.default.existsSync(maybeConfig)) {
932
+ return maybeConfig;
933
+ }
934
+ }
935
+ return null;
936
+ }
937
+
938
+ // src/config/microfrontends/utils/find-package-root.ts
939
+ var import_node_fs2 = __toESM(require("fs"), 1);
940
+ var import_node_path2 = __toESM(require("path"), 1);
941
+ var PACKAGE_JSON = "package.json";
942
+ function findPackageRoot(startDir) {
943
+ let currentDir = startDir || process.cwd();
944
+ while (currentDir !== import_node_path2.default.parse(currentDir).root) {
945
+ const pkgJsonPath = import_node_path2.default.join(currentDir, PACKAGE_JSON);
946
+ if (import_node_fs2.default.existsSync(pkgJsonPath)) {
947
+ return currentDir;
948
+ }
949
+ currentDir = import_node_path2.default.dirname(currentDir);
950
+ }
951
+ throw new Error(
952
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
953
+ );
954
+ }
955
+
956
+ // src/config/microfrontends/utils/find-repository-root.ts
957
+ var import_node_fs3 = __toESM(require("fs"), 1);
958
+ var import_node_path3 = __toESM(require("path"), 1);
959
+ var GIT_DIRECTORY = ".git";
960
+ function hasGitDirectory(dir) {
961
+ const gitPath = import_node_path3.default.join(dir, GIT_DIRECTORY);
962
+ return import_node_fs3.default.existsSync(gitPath) && import_node_fs3.default.statSync(gitPath).isDirectory();
963
+ }
964
+ function hasPnpmWorkspaces(dir) {
965
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "pnpm-workspace.yaml"));
966
+ }
967
+ function hasPackageJson(dir) {
968
+ return import_node_fs3.default.existsSync(import_node_path3.default.join(dir, "package.json"));
969
+ }
970
+ function findRepositoryRoot(startDir) {
971
+ if (process.env.NX_WORKSPACE_ROOT) {
972
+ return process.env.NX_WORKSPACE_ROOT;
973
+ }
974
+ let currentDir = startDir || process.cwd();
975
+ let lastPackageJsonDir = null;
976
+ while (currentDir !== import_node_path3.default.parse(currentDir).root) {
977
+ if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
978
+ return currentDir;
979
+ }
980
+ if (hasPackageJson(currentDir)) {
981
+ lastPackageJsonDir = currentDir;
982
+ }
983
+ currentDir = import_node_path3.default.dirname(currentDir);
984
+ }
985
+ if (lastPackageJsonDir) {
986
+ return lastPackageJsonDir;
987
+ }
988
+ throw new Error(
989
+ `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.`
990
+ );
991
+ }
992
+
1123
993
  // src/config/microfrontends/utils/get-application-context.ts
1124
- var import_node_fs6 = __toESM(require("fs"), 1);
1125
- var import_node_path6 = __toESM(require("path"), 1);
994
+ var import_node_fs4 = __toESM(require("fs"), 1);
995
+ var import_node_path4 = __toESM(require("path"), 1);
1126
996
  function getApplicationContext(opts) {
1127
997
  if (opts?.appName) {
1128
- logger.debug("[MFE Config] Application name from appName parameter:", opts.appName);
998
+ logger.debug(
999
+ "[MFE Config] Application name from appName parameter:",
1000
+ opts.appName
1001
+ );
1129
1002
  return { name: opts.appName };
1130
1003
  }
1131
1004
  if (process.env.VERCEL_PROJECT_NAME) {
1132
- logger.debug("[MFE Config] Application name from VERCEL_PROJECT_NAME:", process.env.VERCEL_PROJECT_NAME);
1005
+ logger.debug(
1006
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
1007
+ process.env.VERCEL_PROJECT_NAME
1008
+ );
1133
1009
  return {
1134
1010
  name: process.env.VERCEL_PROJECT_NAME,
1135
1011
  projectName: process.env.VERCEL_PROJECT_NAME
1136
1012
  };
1137
1013
  }
1138
1014
  if (process.env.NX_TASK_TARGET_PROJECT) {
1139
- logger.debug("[MFE Config] Application name from NX_TASK_TARGET_PROJECT:", process.env.NX_TASK_TARGET_PROJECT);
1015
+ logger.debug(
1016
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
1017
+ process.env.NX_TASK_TARGET_PROJECT
1018
+ );
1140
1019
  return {
1141
1020
  name: process.env.NX_TASK_TARGET_PROJECT,
1142
1021
  packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1143
1022
  };
1144
1023
  }
1145
1024
  try {
1146
- const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1147
- import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1025
+ const vercelProjectJsonPath = import_node_fs4.default.readFileSync(
1026
+ import_node_path4.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1148
1027
  "utf-8"
1149
1028
  );
1150
1029
  const projectJson = JSON.parse(vercelProjectJsonPath);
1151
1030
  if (projectJson.projectName) {
1152
- logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
1031
+ logger.debug(
1032
+ "[MFE Config] Application name from .vercel/project.json:",
1033
+ projectJson.projectName
1034
+ );
1153
1035
  return {
1154
1036
  name: projectJson.projectName,
1155
1037
  projectName: projectJson.projectName
@@ -1158,8 +1040,8 @@ function getApplicationContext(opts) {
1158
1040
  } catch (_) {
1159
1041
  }
1160
1042
  try {
1161
- const packageJsonString = import_node_fs6.default.readFileSync(
1162
- import_node_path6.default.join(opts?.packageRoot || ".", "package.json"),
1043
+ const packageJsonString = import_node_fs4.default.readFileSync(
1044
+ import_node_path4.default.join(opts?.packageRoot || ".", "package.json"),
1163
1045
  "utf-8"
1164
1046
  );
1165
1047
  const packageJson = JSON.parse(packageJsonString);
@@ -1173,7 +1055,10 @@ function getApplicationContext(opts) {
1173
1055
  }
1174
1056
  );
1175
1057
  }
1176
- logger.debug("[MFE Config] Application name from package.json:", packageJson.name);
1058
+ logger.debug(
1059
+ "[MFE Config] Application name from package.json:",
1060
+ packageJson.name
1061
+ );
1177
1062
  return { name: packageJson.name, packageJsonName: packageJson.name };
1178
1063
  } catch (err) {
1179
1064
  throw MicrofrontendError.handle(err, {
@@ -1182,6 +1067,209 @@ function getApplicationContext(opts) {
1182
1067
  }
1183
1068
  }
1184
1069
 
1070
+ // src/config/microfrontends/utils/infer-microfrontends-location.ts
1071
+ var import_node_fs5 = require("fs");
1072
+ var import_node_path5 = require("path");
1073
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
1074
+ var import_jsonc_parser2 = require("jsonc-parser");
1075
+ var configCache = {};
1076
+ function findPackageWithMicrofrontendsConfig({
1077
+ repositoryRoot,
1078
+ applicationContext,
1079
+ customConfigFilename
1080
+ }) {
1081
+ const applicationName = applicationContext.name;
1082
+ logger.debug(
1083
+ "[MFE Config] Searching repository for configs containing application:",
1084
+ applicationName
1085
+ );
1086
+ try {
1087
+ const microfrontendsJsonPaths = import_fast_glob.default.globSync(
1088
+ `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
1089
+ {
1090
+ cwd: repositoryRoot,
1091
+ absolute: true,
1092
+ onlyFiles: true,
1093
+ followSymbolicLinks: false,
1094
+ ignore: ["**/node_modules/**", "**/.git/**"]
1095
+ }
1096
+ );
1097
+ logger.debug(
1098
+ "[MFE Config] Found",
1099
+ microfrontendsJsonPaths.length,
1100
+ "config file(s) in repository"
1101
+ );
1102
+ const matchingPaths = [];
1103
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1104
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1105
+ matchingPaths.push(microfrontendsJsonPath);
1106
+ }
1107
+ }
1108
+ logger.debug(
1109
+ "[MFE Config] Total matching config files:",
1110
+ matchingPaths.length
1111
+ );
1112
+ if (matchingPaths.length > 1) {
1113
+ throw new MicrofrontendError(
1114
+ `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
1115
+ ${matchingPaths.join("\n \u2022 ")}`,
1116
+ { type: "config", subtype: "inference_failed" }
1117
+ );
1118
+ }
1119
+ if (matchingPaths.length === 0) {
1120
+ if (repositoryRoot && doesMisplacedConfigExist(
1121
+ repositoryRoot,
1122
+ applicationName,
1123
+ customConfigFilename
1124
+ )) {
1125
+ logger.debug(
1126
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1127
+ );
1128
+ const misplacedConfigPath = (0, import_node_path5.join)(
1129
+ repositoryRoot,
1130
+ ".vercel",
1131
+ customConfigFilename || "microfrontends.json"
1132
+ );
1133
+ throw new MicrofrontendError(
1134
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1135
+
1136
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1137
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1138
+
1139
+ To fix this:
1140
+ 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
1141
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1142
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1143
+
1144
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1145
+ { type: "config", subtype: "inference_failed" }
1146
+ );
1147
+ }
1148
+ let additionalErrorMessage = "";
1149
+ if (microfrontendsJsonPaths.length > 0) {
1150
+ if (!applicationContext.projectName) {
1151
+ additionalErrorMessage = `
1152
+
1153
+ 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.`;
1154
+ } else {
1155
+ additionalErrorMessage = `
1156
+
1157
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1158
+ }
1159
+ }
1160
+ throw new MicrofrontendError(
1161
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1162
+
1163
+ 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.
1164
+
1165
+ 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.
1166
+
1167
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1168
+ { type: "config", subtype: "inference_failed" }
1169
+ );
1170
+ }
1171
+ const [packageJsonPath] = matchingPaths;
1172
+ return (0, import_node_path5.dirname)(packageJsonPath);
1173
+ } catch (error2) {
1174
+ if (error2 instanceof MicrofrontendError) {
1175
+ throw error2;
1176
+ }
1177
+ return null;
1178
+ }
1179
+ }
1180
+ function inferMicrofrontendsLocation(opts) {
1181
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
1182
+ if (configCache[cacheKey]) {
1183
+ return configCache[cacheKey];
1184
+ }
1185
+ const result = findPackageWithMicrofrontendsConfig(opts);
1186
+ if (!result) {
1187
+ throw new MicrofrontendError(
1188
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1189
+ { type: "config", subtype: "inference_failed" }
1190
+ );
1191
+ }
1192
+ configCache[cacheKey] = result;
1193
+ return result;
1194
+ }
1195
+ function existsSync(path6) {
1196
+ try {
1197
+ (0, import_node_fs5.statSync)(path6);
1198
+ return true;
1199
+ } catch (_) {
1200
+ return false;
1201
+ }
1202
+ }
1203
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1204
+ logger.debug(
1205
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1206
+ );
1207
+ const misplacedConfigPath = (0, import_node_path5.join)(
1208
+ repositoryRoot,
1209
+ ".vercel",
1210
+ customConfigFilename || "microfrontends.json"
1211
+ );
1212
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1213
+ }
1214
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1215
+ try {
1216
+ const microfrontendsJsonContent = (0, import_node_fs5.readFileSync)(
1217
+ microfrontendsJsonPath,
1218
+ "utf-8"
1219
+ );
1220
+ const microfrontendsJson = (0, import_jsonc_parser2.parse)(microfrontendsJsonContent);
1221
+ if (microfrontendsJson.applications[applicationName]) {
1222
+ logger.debug(
1223
+ "[MFE Config] Found application in config:",
1224
+ microfrontendsJsonPath
1225
+ );
1226
+ return true;
1227
+ }
1228
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1229
+ if (app.packageName === applicationName) {
1230
+ logger.debug(
1231
+ "[MFE Config] Found application via packageName in config:",
1232
+ microfrontendsJsonPath
1233
+ );
1234
+ return true;
1235
+ }
1236
+ }
1237
+ } catch (error2) {
1238
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1239
+ }
1240
+ return false;
1241
+ }
1242
+
1243
+ // src/config/microfrontends/utils/is-monorepo.ts
1244
+ var import_node_fs6 = __toESM(require("fs"), 1);
1245
+ var import_node_path6 = __toESM(require("path"), 1);
1246
+ function isMonorepo({
1247
+ repositoryRoot
1248
+ }) {
1249
+ try {
1250
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1251
+ return true;
1252
+ }
1253
+ if (import_node_fs6.default.existsSync(import_node_path6.default.join(repositoryRoot, "vlt-workspaces.json"))) {
1254
+ return true;
1255
+ }
1256
+ if (process.env.NX_WORKSPACE_ROOT === import_node_path6.default.resolve(repositoryRoot)) {
1257
+ return true;
1258
+ }
1259
+ const packageJsonPath = import_node_path6.default.join(repositoryRoot, "package.json");
1260
+ if (!import_node_fs6.default.existsSync(packageJsonPath)) {
1261
+ return false;
1262
+ }
1263
+ const packageJson = JSON.parse(
1264
+ import_node_fs6.default.readFileSync(packageJsonPath, "utf-8")
1265
+ );
1266
+ return packageJson.workspaces !== void 0;
1267
+ } catch (error2) {
1268
+ logger.error("Error determining if repository is a monorepo", error2);
1269
+ return false;
1270
+ }
1271
+ }
1272
+
1185
1273
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1186
1274
  var import_node_path7 = __toESM(require("path"), 1);
1187
1275
 
@@ -1195,8 +1283,8 @@ function getOutputFilePath() {
1195
1283
  }
1196
1284
 
1197
1285
  // src/config/microfrontends/server/validation.ts
1198
- var import_jsonc_parser3 = require("jsonc-parser");
1199
1286
  var import_ajv = require("ajv");
1287
+ var import_jsonc_parser3 = require("jsonc-parser");
1200
1288
 
1201
1289
  // schema/schema.json
1202
1290
  var schema_default = {
@@ -1224,9 +1312,7 @@ var schema_default = {
1224
1312
  description: "Optional configuration options for the microfrontend."
1225
1313
  }
1226
1314
  },
1227
- required: [
1228
- "applications"
1229
- ],
1315
+ required: ["applications"],
1230
1316
  additionalProperties: false,
1231
1317
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1232
1318
  },
@@ -1263,19 +1349,14 @@ var schema_default = {
1263
1349
  description: "Development configuration for the default application."
1264
1350
  }
1265
1351
  },
1266
- required: [
1267
- "development"
1268
- ],
1352
+ required: ["development"],
1269
1353
  additionalProperties: false
1270
1354
  },
1271
1355
  DefaultDevelopment: {
1272
1356
  type: "object",
1273
1357
  properties: {
1274
1358
  local: {
1275
- type: [
1276
- "number",
1277
- "string"
1278
- ],
1359
+ type: ["number", "string"],
1279
1360
  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."
1280
1361
  },
1281
1362
  task: {
@@ -1287,9 +1368,7 @@ var schema_default = {
1287
1368
  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."
1288
1369
  }
1289
1370
  },
1290
- required: [
1291
- "fallback"
1292
- ],
1371
+ required: ["fallback"],
1293
1372
  additionalProperties: false
1294
1373
  },
1295
1374
  ChildApplication: {
@@ -1312,19 +1391,14 @@ var schema_default = {
1312
1391
  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."
1313
1392
  }
1314
1393
  },
1315
- required: [
1316
- "routing"
1317
- ],
1394
+ required: ["routing"],
1318
1395
  additionalProperties: false
1319
1396
  },
1320
1397
  ChildDevelopment: {
1321
1398
  type: "object",
1322
1399
  properties: {
1323
1400
  local: {
1324
- type: [
1325
- "number",
1326
- "string"
1327
- ],
1401
+ type: ["number", "string"],
1328
1402
  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."
1329
1403
  },
1330
1404
  task: {
@@ -1364,9 +1438,7 @@ var schema_default = {
1364
1438
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1365
1439
  }
1366
1440
  },
1367
- required: [
1368
- "paths"
1369
- ],
1441
+ required: ["paths"],
1370
1442
  additionalProperties: false,
1371
1443
  description: "A group of paths that is routed to this application."
1372
1444
  },