@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
@@ -1,39 +1,28 @@
1
1
  // src/config/microfrontends/server/index.ts
2
2
  import fs6 from "node:fs";
3
- import { dirname as dirname2, join as join2, resolve } from "node:path";
3
+ import { dirname as dirname2, join as join3, resolve } from "node:path";
4
4
 
5
- // src/config/overrides/constants.ts
6
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
7
- var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
8
-
9
- // src/config/overrides/is-override-cookie.ts
10
- function isOverrideCookie(cookie) {
11
- return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
5
+ // src/bin/logger.ts
6
+ function debug(...args) {
7
+ if (process.env.MFE_DEBUG) {
8
+ console.log(...args);
9
+ }
12
10
  }
13
-
14
- // src/config/overrides/get-override-from-cookie.ts
15
- function getOverrideFromCookie(cookie) {
16
- if (!isOverrideCookie(cookie) || !cookie.value)
17
- return;
18
- return {
19
- application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
20
- host: cookie.value
21
- };
11
+ function info(...args) {
12
+ console.log(...args);
22
13
  }
23
-
24
- // src/config/overrides/parse-overrides.ts
25
- function parseOverrides(cookies) {
26
- const overridesConfig = { applications: {} };
27
- cookies.forEach((cookie) => {
28
- const override = getOverrideFromCookie(cookie);
29
- if (!override)
30
- return;
31
- overridesConfig.applications[override.application] = {
32
- environment: { host: override.host }
33
- };
34
- });
35
- return overridesConfig;
14
+ function warn(...args) {
15
+ console.warn(...args);
36
16
  }
17
+ function error(...args) {
18
+ console.error(...args);
19
+ }
20
+ var logger = {
21
+ debug,
22
+ info,
23
+ warn,
24
+ error
25
+ };
37
26
 
38
27
  // src/config/errors.ts
39
28
  var MicrofrontendError = class extends Error {
@@ -127,296 +116,47 @@ var MicrofrontendError = class extends Error {
127
116
  }
128
117
  };
129
118
 
130
- // src/config/microfrontends-config/utils/get-config-from-env.ts
131
- function getConfigStringFromEnv() {
132
- const config = process.env.MFE_CONFIG;
133
- if (!config) {
134
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
135
- type: "config",
136
- subtype: "not_found_in_env"
137
- });
138
- }
139
- return config;
140
- }
141
-
142
- // src/config/schema/utils/is-default-app.ts
143
- function isDefaultApp(a) {
144
- return !("routing" in a);
145
- }
146
-
147
- // src/config/microfrontends/utils/find-repository-root.ts
148
- import fs from "node:fs";
149
- import path from "node:path";
150
- var GIT_DIRECTORY = ".git";
151
- function hasGitDirectory(dir) {
152
- const gitPath = path.join(dir, GIT_DIRECTORY);
153
- return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
154
- }
155
- function hasPnpmWorkspaces(dir) {
156
- return fs.existsSync(path.join(dir, "pnpm-workspace.yaml"));
157
- }
158
- function hasPackageJson(dir) {
159
- return fs.existsSync(path.join(dir, "package.json"));
160
- }
161
- function findRepositoryRoot(startDir) {
162
- if (process.env.NX_WORKSPACE_ROOT) {
163
- return process.env.NX_WORKSPACE_ROOT;
164
- }
165
- let currentDir = startDir || process.cwd();
166
- let lastPackageJsonDir = null;
167
- while (currentDir !== path.parse(currentDir).root) {
168
- if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
169
- return currentDir;
170
- }
171
- if (hasPackageJson(currentDir)) {
172
- lastPackageJsonDir = currentDir;
173
- }
174
- currentDir = path.dirname(currentDir);
175
- }
176
- if (lastPackageJsonDir) {
177
- return lastPackageJsonDir;
178
- }
179
- throw new Error(
180
- `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.`
181
- );
182
- }
183
-
184
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
185
- import { dirname } from "node:path";
186
- import { readFileSync } from "node:fs";
119
+ // src/config/microfrontends-config/isomorphic/index.ts
187
120
  import { parse } from "jsonc-parser";
188
- import fg from "fast-glob";
189
121
 
190
- // src/bin/logger.ts
191
- function debug(...args) {
192
- if (process.env.MFE_DEBUG) {
193
- console.log(...args);
194
- }
195
- }
196
- function info(...args) {
197
- console.log(...args);
198
- }
199
- function warn(...args) {
200
- console.warn(...args);
201
- }
202
- function error(...args) {
203
- console.error(...args);
204
- }
205
- var logger = {
206
- debug,
207
- info,
208
- warn,
209
- error
210
- };
211
-
212
- // src/config/microfrontends/utils/get-config-file-name.ts
213
- var DEFAULT_CONFIGURATION_FILENAMES = [
214
- "microfrontends.json",
215
- "microfrontends.jsonc"
216
- ];
217
- function getPossibleConfigurationFilenames({
218
- customConfigFilename
219
- }) {
220
- if (customConfigFilename) {
221
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
222
- throw new Error(
223
- `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.`
224
- );
225
- }
226
- return Array.from(
227
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
228
- );
229
- }
230
- return DEFAULT_CONFIGURATION_FILENAMES;
231
- }
232
-
233
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
234
- var configCache = {};
235
- function findPackageWithMicrofrontendsConfig({
236
- repositoryRoot,
237
- applicationContext,
238
- customConfigFilename
239
- }) {
240
- const applicationName = applicationContext.name;
241
- logger.debug(
242
- "[MFE Config] Searching repository for configs containing application:",
243
- applicationName
244
- );
245
- try {
246
- const microfrontendsJsonPaths = fg.globSync(
247
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
248
- {
249
- cwd: repositoryRoot,
250
- absolute: true,
251
- onlyFiles: true,
252
- followSymbolicLinks: false,
253
- ignore: ["**/node_modules/**", "**/.git/**"]
254
- }
255
- );
256
- logger.debug(
257
- "[MFE Config] Found",
258
- microfrontendsJsonPaths.length,
259
- "config file(s) in repository"
260
- );
261
- const matchingPaths = [];
262
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
263
- try {
264
- const microfrontendsJsonContent = readFileSync(
265
- microfrontendsJsonPath,
266
- "utf-8"
267
- );
268
- const microfrontendsJson = parse(microfrontendsJsonContent);
269
- if (microfrontendsJson.applications[applicationName]) {
270
- logger.debug(
271
- "[MFE Config] Found application in config:",
272
- microfrontendsJsonPath
273
- );
274
- matchingPaths.push(microfrontendsJsonPath);
275
- } else {
276
- for (const [_, app] of Object.entries(
277
- microfrontendsJson.applications
278
- )) {
279
- if (app.packageName === applicationName) {
280
- logger.debug(
281
- "[MFE Config] Found application via packageName in config:",
282
- microfrontendsJsonPath
283
- );
284
- matchingPaths.push(microfrontendsJsonPath);
285
- }
286
- }
287
- }
288
- } catch (error2) {
289
- }
290
- }
291
- logger.debug(
292
- "[MFE Config] Total matching config files:",
293
- matchingPaths.length
294
- );
295
- if (matchingPaths.length > 1) {
296
- throw new MicrofrontendError(
297
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
298
- ${matchingPaths.join("\n \u2022 ")}`,
299
- { type: "config", subtype: "inference_failed" }
300
- );
301
- }
302
- if (matchingPaths.length === 0) {
303
- let additionalErrorMessage = "";
304
- if (microfrontendsJsonPaths.length > 0) {
305
- if (!applicationContext.projectName) {
306
- additionalErrorMessage = `
307
-
308
- 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.`;
309
- } else {
310
- additionalErrorMessage = `
311
-
312
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
313
- }
314
- }
315
- throw new MicrofrontendError(
316
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
317
-
318
- 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.
319
-
320
- 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.
122
+ // src/config/overrides/constants.ts
123
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
124
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
321
125
 
322
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
323
- { type: "config", subtype: "inference_failed" }
324
- );
325
- }
326
- const [packageJsonPath] = matchingPaths;
327
- return dirname(packageJsonPath);
328
- } catch (error2) {
329
- if (error2 instanceof MicrofrontendError) {
330
- throw error2;
331
- }
332
- return null;
333
- }
334
- }
335
- function inferMicrofrontendsLocation(opts) {
336
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
337
- if (configCache[cacheKey]) {
338
- return configCache[cacheKey];
339
- }
340
- const result = findPackageWithMicrofrontendsConfig(opts);
341
- if (!result) {
342
- throw new MicrofrontendError(
343
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
344
- { type: "config", subtype: "inference_failed" }
345
- );
346
- }
347
- configCache[cacheKey] = result;
348
- return result;
126
+ // src/config/overrides/is-override-cookie.ts
127
+ function isOverrideCookie(cookie) {
128
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
349
129
  }
350
130
 
351
- // src/config/microfrontends/utils/is-monorepo.ts
352
- import fs2 from "node:fs";
353
- import path2 from "node:path";
354
- function isMonorepo({
355
- repositoryRoot
356
- }) {
357
- try {
358
- if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
359
- return true;
360
- }
361
- if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
362
- return true;
363
- }
364
- if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
365
- return true;
366
- }
367
- const packageJsonPath = path2.join(repositoryRoot, "package.json");
368
- if (!fs2.existsSync(packageJsonPath)) {
369
- return false;
370
- }
371
- const packageJson = JSON.parse(
372
- fs2.readFileSync(packageJsonPath, "utf-8")
373
- );
374
- return packageJson.workspaces !== void 0;
375
- } catch (error2) {
376
- logger.error("Error determining if repository is a monorepo", error2);
377
- return false;
378
- }
131
+ // src/config/overrides/get-override-from-cookie.ts
132
+ function getOverrideFromCookie(cookie) {
133
+ if (!isOverrideCookie(cookie) || !cookie.value)
134
+ return;
135
+ return {
136
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
137
+ host: cookie.value
138
+ };
379
139
  }
380
140
 
381
- // src/config/microfrontends/utils/find-package-root.ts
382
- import fs3 from "node:fs";
383
- import path3 from "node:path";
384
- var PACKAGE_JSON = "package.json";
385
- function findPackageRoot(startDir) {
386
- let currentDir = startDir || process.cwd();
387
- while (currentDir !== path3.parse(currentDir).root) {
388
- const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
389
- if (fs3.existsSync(pkgJsonPath)) {
390
- return currentDir;
391
- }
392
- currentDir = path3.dirname(currentDir);
393
- }
394
- throw new Error(
395
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
396
- );
141
+ // src/config/overrides/parse-overrides.ts
142
+ function parseOverrides(cookies) {
143
+ const overridesConfig = { applications: {} };
144
+ cookies.forEach((cookie) => {
145
+ const override = getOverrideFromCookie(cookie);
146
+ if (!override)
147
+ return;
148
+ overridesConfig.applications[override.application] = {
149
+ environment: { host: override.host }
150
+ };
151
+ });
152
+ return overridesConfig;
397
153
  }
398
154
 
399
- // src/config/microfrontends/utils/find-config.ts
400
- import fs4 from "node:fs";
401
- import { join } from "node:path";
402
- function findConfig({
403
- dir,
404
- customConfigFilename
405
- }) {
406
- for (const filename of getPossibleConfigurationFilenames({
407
- customConfigFilename
408
- })) {
409
- const maybeConfig = join(dir, filename);
410
- if (fs4.existsSync(maybeConfig)) {
411
- return maybeConfig;
412
- }
413
- }
414
- return null;
155
+ // src/config/schema/utils/is-default-app.ts
156
+ function isDefaultApp(a) {
157
+ return !("routing" in a);
415
158
  }
416
159
 
417
- // src/config/microfrontends-config/isomorphic/index.ts
418
- import { parse as parse2 } from "jsonc-parser";
419
-
420
160
  // src/config/microfrontends-config/client/index.ts
421
161
  import { pathToRegexp } from "path-to-regexp";
422
162
  var regexpCache = /* @__PURE__ */ new Map();
@@ -515,8 +255,185 @@ var MicrofrontendConfigClient = class {
515
255
  }
516
256
  };
517
257
 
258
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
259
+ function getConfigStringFromEnv() {
260
+ const config = process.env.MFE_CONFIG;
261
+ if (!config) {
262
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
263
+ type: "config",
264
+ subtype: "not_found_in_env"
265
+ });
266
+ }
267
+ return config;
268
+ }
269
+
270
+ // src/config/microfrontends-config/isomorphic/constants.ts
271
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
272
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
273
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
274
+
275
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
276
+ function generatePortFromName({
277
+ name,
278
+ minPort = 3e3,
279
+ maxPort = 8e3
280
+ }) {
281
+ if (!name) {
282
+ throw new Error("Name is required to generate a port");
283
+ }
284
+ let hash = 0;
285
+ for (let i = 0; i < name.length; i++) {
286
+ hash = (hash << 5) - hash + name.charCodeAt(i);
287
+ hash |= 0;
288
+ }
289
+ hash = Math.abs(hash);
290
+ const range = maxPort - minPort;
291
+ const port = minPort + hash % range;
292
+ return port;
293
+ }
294
+
295
+ // src/config/microfrontends-config/isomorphic/host.ts
296
+ var Host = class {
297
+ constructor(hostConfig, options) {
298
+ if (typeof hostConfig === "string") {
299
+ ({
300
+ protocol: this.protocol,
301
+ host: this.host,
302
+ port: this.port
303
+ } = Host.parseUrl(hostConfig));
304
+ } else {
305
+ const { protocol = "https", host, port } = hostConfig;
306
+ this.protocol = protocol;
307
+ this.host = host;
308
+ this.port = port;
309
+ }
310
+ this.local = options?.isLocal;
311
+ }
312
+ static parseUrl(url, defaultProtocol = "https") {
313
+ let hostToParse = url;
314
+ if (!/^https?:\/\//.exec(hostToParse)) {
315
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
316
+ }
317
+ const parsed = new URL(hostToParse);
318
+ if (!parsed.hostname) {
319
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
320
+ }
321
+ if (parsed.hash) {
322
+ throw new Error(
323
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
324
+ );
325
+ }
326
+ if (parsed.username || parsed.password) {
327
+ throw new Error(
328
+ Host.getMicrofrontendsError(
329
+ url,
330
+ "cannot have authentication credentials (username and/or password)"
331
+ )
332
+ );
333
+ }
334
+ if (parsed.pathname !== "/") {
335
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
336
+ }
337
+ if (parsed.search) {
338
+ throw new Error(
339
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
340
+ );
341
+ }
342
+ const protocol = parsed.protocol.slice(0, -1);
343
+ return {
344
+ protocol,
345
+ host: parsed.hostname,
346
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
347
+ };
348
+ }
349
+ static getMicrofrontendsError(url, message) {
350
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
351
+ }
352
+ isLocal() {
353
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
354
+ }
355
+ toString() {
356
+ const url = this.toUrl();
357
+ return url.toString().replace(/\/$/, "");
358
+ }
359
+ toUrl() {
360
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
361
+ return new URL(url);
362
+ }
363
+ };
364
+ var LocalHost = class extends Host {
365
+ constructor({
366
+ appName,
367
+ local
368
+ }) {
369
+ const portOverride = process.env[MFE_APP_PORT_ENV];
370
+ if (portOverride) {
371
+ const overridePort = Number.parseInt(portOverride, 10);
372
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
373
+ super({
374
+ protocol: "http",
375
+ host: "localhost",
376
+ port: overridePort
377
+ });
378
+ return;
379
+ }
380
+ }
381
+ let protocol;
382
+ let host;
383
+ let port;
384
+ if (typeof local === "number") {
385
+ port = local;
386
+ } else if (typeof local === "string") {
387
+ if (/^\d+$/.test(local)) {
388
+ port = Number.parseInt(local, 10);
389
+ } else {
390
+ const parsed = Host.parseUrl(local, "http");
391
+ protocol = parsed.protocol;
392
+ host = parsed.host;
393
+ port = parsed.port;
394
+ }
395
+ } else if (local) {
396
+ protocol = local.protocol;
397
+ host = local.host;
398
+ port = local.port;
399
+ }
400
+ super({
401
+ protocol: protocol ?? "http",
402
+ host: host ?? "localhost",
403
+ port: port ?? generatePortFromName({ name: appName })
404
+ });
405
+ }
406
+ };
407
+
408
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
409
+ import md5 from "md5";
410
+ function hashApplicationName(name) {
411
+ if (!name) {
412
+ throw new Error("Application name is required to generate hash");
413
+ }
414
+ return md5(name).substring(0, 6).padStart(6, "0");
415
+ }
416
+
417
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
418
+ var PREFIX = "vc-ap";
419
+ function generateAssetPrefixFromName({
420
+ name
421
+ }) {
422
+ if (!name) {
423
+ throw new Error("Name is required to generate an asset prefix");
424
+ }
425
+ return `${PREFIX}-${hashApplicationName(name)}`;
426
+ }
427
+
428
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
429
+ function generateAutomationBypassEnvVarName({
430
+ name
431
+ }) {
432
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
433
+ }
434
+
518
435
  // src/config/microfrontends-config/isomorphic/validation.ts
519
- import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
436
+ import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
520
437
  var LIST_FORMATTER = new Intl.ListFormat("en", {
521
438
  style: "long",
522
439
  type: "conjunction"
@@ -696,154 +613,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
696
613
  }
697
614
  };
698
615
 
699
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
700
- import md5 from "md5";
701
- function hashApplicationName(name) {
702
- if (!name) {
703
- throw new Error("Application name is required to generate hash");
704
- }
705
- return md5(name).substring(0, 6).padStart(6, "0");
706
- }
707
-
708
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
709
- var PREFIX = "vc-ap";
710
- function generateAssetPrefixFromName({
711
- name
712
- }) {
713
- if (!name) {
714
- throw new Error("Name is required to generate an asset prefix");
715
- }
716
- return `${PREFIX}-${hashApplicationName(name)}`;
717
- }
718
-
719
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
720
- function generatePortFromName({
721
- name,
722
- minPort = 3e3,
723
- maxPort = 8e3
724
- }) {
725
- if (!name) {
726
- throw new Error("Name is required to generate a port");
727
- }
728
- let hash = 0;
729
- for (let i = 0; i < name.length; i++) {
730
- hash = (hash << 5) - hash + name.charCodeAt(i);
731
- hash |= 0;
732
- }
733
- hash = Math.abs(hash);
734
- const range = maxPort - minPort;
735
- const port = minPort + hash % range;
736
- return port;
737
- }
738
-
739
- // src/config/microfrontends-config/isomorphic/host.ts
740
- var Host = class {
741
- constructor(hostConfig, options) {
742
- if (typeof hostConfig === "string") {
743
- ({
744
- protocol: this.protocol,
745
- host: this.host,
746
- port: this.port
747
- } = Host.parseUrl(hostConfig));
748
- } else {
749
- const { protocol = "https", host, port } = hostConfig;
750
- this.protocol = protocol;
751
- this.host = host;
752
- this.port = port;
753
- }
754
- this.local = options?.isLocal;
755
- }
756
- static parseUrl(url, defaultProtocol = "https") {
757
- let hostToParse = url;
758
- if (!/^https?:\/\//.exec(hostToParse)) {
759
- hostToParse = `${defaultProtocol}://${hostToParse}`;
760
- }
761
- const parsed = new URL(hostToParse);
762
- if (!parsed.hostname) {
763
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
764
- }
765
- if (parsed.hash) {
766
- throw new Error(
767
- Host.getMicrofrontendsError(url, "cannot have a fragment")
768
- );
769
- }
770
- if (parsed.username || parsed.password) {
771
- throw new Error(
772
- Host.getMicrofrontendsError(
773
- url,
774
- "cannot have authentication credentials (username and/or password)"
775
- )
776
- );
777
- }
778
- if (parsed.pathname !== "/") {
779
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
780
- }
781
- if (parsed.search) {
782
- throw new Error(
783
- Host.getMicrofrontendsError(url, "cannot have query parameters")
784
- );
785
- }
786
- const protocol = parsed.protocol.slice(0, -1);
787
- return {
788
- protocol,
789
- host: parsed.hostname,
790
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
791
- };
792
- }
793
- static getMicrofrontendsError(url, message) {
794
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
795
- }
796
- isLocal() {
797
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
798
- }
799
- toString() {
800
- const url = this.toUrl();
801
- return url.toString().replace(/\/$/, "");
802
- }
803
- toUrl() {
804
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
805
- return new URL(url);
806
- }
807
- };
808
- var LocalHost = class extends Host {
809
- constructor({
810
- appName,
811
- local
812
- }) {
813
- let protocol;
814
- let host;
815
- let port;
816
- if (typeof local === "number") {
817
- port = local;
818
- } else if (typeof local === "string") {
819
- if (/^\d+$/.test(local)) {
820
- port = Number.parseInt(local);
821
- } else {
822
- const parsed = Host.parseUrl(local, "http");
823
- protocol = parsed.protocol;
824
- host = parsed.host;
825
- port = parsed.port;
826
- }
827
- } else if (local) {
828
- protocol = local.protocol;
829
- host = local.host;
830
- port = local.port;
831
- }
832
- super({
833
- protocol: protocol ?? "http",
834
- host: host ?? "localhost",
835
- port: port ?? generatePortFromName({ name: appName })
836
- });
837
- }
838
- };
839
-
840
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
841
- function generateAutomationBypassEnvVarName({
842
- name
843
- }) {
844
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
845
- }
846
-
847
616
  // src/config/microfrontends-config/isomorphic/application.ts
848
617
  var Application = class {
849
618
  constructor(name, {
@@ -924,9 +693,6 @@ var ChildApplication = class extends Application {
924
693
  }
925
694
  };
926
695
 
927
- // src/config/microfrontends-config/isomorphic/constants.ts
928
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
929
-
930
696
  // src/config/microfrontends-config/isomorphic/index.ts
931
697
  var MicrofrontendConfigIsomorphic = class {
932
698
  constructor({
@@ -970,7 +736,7 @@ var MicrofrontendConfigIsomorphic = class {
970
736
  };
971
737
  }
972
738
  static validate(config) {
973
- const c = typeof config === "string" ? parse2(config) : config;
739
+ const c = typeof config === "string" ? parse(config) : config;
974
740
  validateConfigPaths(c.applications);
975
741
  validateConfigDefaultApplication(c.applications);
976
742
  return c;
@@ -979,7 +745,7 @@ var MicrofrontendConfigIsomorphic = class {
979
745
  cookies
980
746
  }) {
981
747
  return new MicrofrontendConfigIsomorphic({
982
- config: parse2(getConfigStringFromEnv()),
748
+ config: parse(getConfigStringFromEnv()),
983
749
  overrides: parseOverrides(cookies ?? [])
984
750
  });
985
751
  }
@@ -1044,77 +810,193 @@ var MicrofrontendConfigIsomorphic = class {
1044
810
  getDefaultApplication() {
1045
811
  return this.defaultApplication;
1046
812
  }
1047
- /**
1048
- * Returns the configured port for the local proxy
1049
- */
1050
- getLocalProxyPort() {
1051
- return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
813
+ /**
814
+ * Returns the configured port for the local proxy.
815
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
816
+ */
817
+ getLocalProxyPort() {
818
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
819
+ if (portOverride) {
820
+ const port = Number.parseInt(portOverride, 10);
821
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
822
+ return port;
823
+ }
824
+ }
825
+ return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
826
+ }
827
+ toClientConfig(options) {
828
+ const applications = Object.fromEntries(
829
+ Object.entries(this.childApplications).map(([name, application]) => [
830
+ hashApplicationName(name),
831
+ {
832
+ default: false,
833
+ routing: application.routing
834
+ }
835
+ ])
836
+ );
837
+ applications[hashApplicationName(this.defaultApplication.name)] = {
838
+ default: true
839
+ };
840
+ return new MicrofrontendConfigClient(
841
+ {
842
+ applications
843
+ },
844
+ {
845
+ removeFlaggedPaths: options?.removeFlaggedPaths
846
+ }
847
+ );
848
+ }
849
+ /**
850
+ * Serializes the class back to the Schema type.
851
+ *
852
+ * NOTE: This is used when writing the config to disk and must always match the input Schema
853
+ */
854
+ toSchemaJson() {
855
+ return this.serialized.config;
856
+ }
857
+ serialize() {
858
+ return this.serialized;
859
+ }
860
+ };
861
+
862
+ // src/config/microfrontends/utils/find-config.ts
863
+ import fs from "node:fs";
864
+ import { join } from "node:path";
865
+
866
+ // src/config/microfrontends/utils/get-config-file-name.ts
867
+ var DEFAULT_CONFIGURATION_FILENAMES = [
868
+ "microfrontends.json",
869
+ "microfrontends.jsonc"
870
+ ];
871
+ function getPossibleConfigurationFilenames({
872
+ customConfigFilename
873
+ }) {
874
+ if (customConfigFilename) {
875
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
876
+ throw new Error(
877
+ `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.`
878
+ );
879
+ }
880
+ return Array.from(
881
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
882
+ );
883
+ }
884
+ return DEFAULT_CONFIGURATION_FILENAMES;
885
+ }
886
+
887
+ // src/config/microfrontends/utils/find-config.ts
888
+ function findConfig({
889
+ dir,
890
+ customConfigFilename
891
+ }) {
892
+ for (const filename of getPossibleConfigurationFilenames({
893
+ customConfigFilename
894
+ })) {
895
+ const maybeConfig = join(dir, filename);
896
+ if (fs.existsSync(maybeConfig)) {
897
+ return maybeConfig;
898
+ }
899
+ }
900
+ return null;
901
+ }
902
+
903
+ // src/config/microfrontends/utils/find-package-root.ts
904
+ import fs2 from "node:fs";
905
+ import path from "node:path";
906
+ var PACKAGE_JSON = "package.json";
907
+ function findPackageRoot(startDir) {
908
+ let currentDir = startDir || process.cwd();
909
+ while (currentDir !== path.parse(currentDir).root) {
910
+ const pkgJsonPath = path.join(currentDir, PACKAGE_JSON);
911
+ if (fs2.existsSync(pkgJsonPath)) {
912
+ return currentDir;
913
+ }
914
+ currentDir = path.dirname(currentDir);
1052
915
  }
1053
- toClientConfig(options) {
1054
- const applications = Object.fromEntries(
1055
- Object.entries(this.childApplications).map(([name, application]) => [
1056
- hashApplicationName(name),
1057
- {
1058
- default: false,
1059
- routing: application.routing
1060
- }
1061
- ])
1062
- );
1063
- applications[hashApplicationName(this.defaultApplication.name)] = {
1064
- default: true
1065
- };
1066
- return new MicrofrontendConfigClient(
1067
- {
1068
- applications
1069
- },
1070
- {
1071
- removeFlaggedPaths: options?.removeFlaggedPaths
1072
- }
1073
- );
916
+ throw new Error(
917
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
918
+ );
919
+ }
920
+
921
+ // src/config/microfrontends/utils/find-repository-root.ts
922
+ import fs3 from "node:fs";
923
+ import path2 from "node:path";
924
+ var GIT_DIRECTORY = ".git";
925
+ function hasGitDirectory(dir) {
926
+ const gitPath = path2.join(dir, GIT_DIRECTORY);
927
+ return fs3.existsSync(gitPath) && fs3.statSync(gitPath).isDirectory();
928
+ }
929
+ function hasPnpmWorkspaces(dir) {
930
+ return fs3.existsSync(path2.join(dir, "pnpm-workspace.yaml"));
931
+ }
932
+ function hasPackageJson(dir) {
933
+ return fs3.existsSync(path2.join(dir, "package.json"));
934
+ }
935
+ function findRepositoryRoot(startDir) {
936
+ if (process.env.NX_WORKSPACE_ROOT) {
937
+ return process.env.NX_WORKSPACE_ROOT;
1074
938
  }
1075
- /**
1076
- * Serializes the class back to the Schema type.
1077
- *
1078
- * NOTE: This is used when writing the config to disk and must always match the input Schema
1079
- */
1080
- toSchemaJson() {
1081
- return this.serialized.config;
939
+ let currentDir = startDir || process.cwd();
940
+ let lastPackageJsonDir = null;
941
+ while (currentDir !== path2.parse(currentDir).root) {
942
+ if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
943
+ return currentDir;
944
+ }
945
+ if (hasPackageJson(currentDir)) {
946
+ lastPackageJsonDir = currentDir;
947
+ }
948
+ currentDir = path2.dirname(currentDir);
1082
949
  }
1083
- serialize() {
1084
- return this.serialized;
950
+ if (lastPackageJsonDir) {
951
+ return lastPackageJsonDir;
1085
952
  }
1086
- };
953
+ throw new Error(
954
+ `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.`
955
+ );
956
+ }
1087
957
 
1088
958
  // src/config/microfrontends/utils/get-application-context.ts
1089
- import fs5 from "node:fs";
1090
- import path4 from "node:path";
959
+ import fs4 from "node:fs";
960
+ import path3 from "node:path";
1091
961
  function getApplicationContext(opts) {
1092
962
  if (opts?.appName) {
1093
- logger.debug("[MFE Config] Application name from appName parameter:", opts.appName);
963
+ logger.debug(
964
+ "[MFE Config] Application name from appName parameter:",
965
+ opts.appName
966
+ );
1094
967
  return { name: opts.appName };
1095
968
  }
1096
969
  if (process.env.VERCEL_PROJECT_NAME) {
1097
- logger.debug("[MFE Config] Application name from VERCEL_PROJECT_NAME:", process.env.VERCEL_PROJECT_NAME);
970
+ logger.debug(
971
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
972
+ process.env.VERCEL_PROJECT_NAME
973
+ );
1098
974
  return {
1099
975
  name: process.env.VERCEL_PROJECT_NAME,
1100
976
  projectName: process.env.VERCEL_PROJECT_NAME
1101
977
  };
1102
978
  }
1103
979
  if (process.env.NX_TASK_TARGET_PROJECT) {
1104
- logger.debug("[MFE Config] Application name from NX_TASK_TARGET_PROJECT:", process.env.NX_TASK_TARGET_PROJECT);
980
+ logger.debug(
981
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
982
+ process.env.NX_TASK_TARGET_PROJECT
983
+ );
1105
984
  return {
1106
985
  name: process.env.NX_TASK_TARGET_PROJECT,
1107
986
  packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1108
987
  };
1109
988
  }
1110
989
  try {
1111
- const vercelProjectJsonPath = fs5.readFileSync(
1112
- path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
990
+ const vercelProjectJsonPath = fs4.readFileSync(
991
+ path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1113
992
  "utf-8"
1114
993
  );
1115
994
  const projectJson = JSON.parse(vercelProjectJsonPath);
1116
995
  if (projectJson.projectName) {
1117
- logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
996
+ logger.debug(
997
+ "[MFE Config] Application name from .vercel/project.json:",
998
+ projectJson.projectName
999
+ );
1118
1000
  return {
1119
1001
  name: projectJson.projectName,
1120
1002
  projectName: projectJson.projectName
@@ -1123,8 +1005,8 @@ function getApplicationContext(opts) {
1123
1005
  } catch (_) {
1124
1006
  }
1125
1007
  try {
1126
- const packageJsonString = fs5.readFileSync(
1127
- path4.join(opts?.packageRoot || ".", "package.json"),
1008
+ const packageJsonString = fs4.readFileSync(
1009
+ path3.join(opts?.packageRoot || ".", "package.json"),
1128
1010
  "utf-8"
1129
1011
  );
1130
1012
  const packageJson = JSON.parse(packageJsonString);
@@ -1138,7 +1020,10 @@ function getApplicationContext(opts) {
1138
1020
  }
1139
1021
  );
1140
1022
  }
1141
- logger.debug("[MFE Config] Application name from package.json:", packageJson.name);
1023
+ logger.debug(
1024
+ "[MFE Config] Application name from package.json:",
1025
+ packageJson.name
1026
+ );
1142
1027
  return { name: packageJson.name, packageJsonName: packageJson.name };
1143
1028
  } catch (err) {
1144
1029
  throw MicrofrontendError.handle(err, {
@@ -1147,6 +1032,209 @@ function getApplicationContext(opts) {
1147
1032
  }
1148
1033
  }
1149
1034
 
1035
+ // src/config/microfrontends/utils/infer-microfrontends-location.ts
1036
+ import { readFileSync, statSync } from "node:fs";
1037
+ import { dirname, join as join2 } from "node:path";
1038
+ import fg from "fast-glob";
1039
+ import { parse as parse2 } from "jsonc-parser";
1040
+ var configCache = {};
1041
+ function findPackageWithMicrofrontendsConfig({
1042
+ repositoryRoot,
1043
+ applicationContext,
1044
+ customConfigFilename
1045
+ }) {
1046
+ const applicationName = applicationContext.name;
1047
+ logger.debug(
1048
+ "[MFE Config] Searching repository for configs containing application:",
1049
+ applicationName
1050
+ );
1051
+ try {
1052
+ const microfrontendsJsonPaths = fg.globSync(
1053
+ `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
1054
+ {
1055
+ cwd: repositoryRoot,
1056
+ absolute: true,
1057
+ onlyFiles: true,
1058
+ followSymbolicLinks: false,
1059
+ ignore: ["**/node_modules/**", "**/.git/**"]
1060
+ }
1061
+ );
1062
+ logger.debug(
1063
+ "[MFE Config] Found",
1064
+ microfrontendsJsonPaths.length,
1065
+ "config file(s) in repository"
1066
+ );
1067
+ const matchingPaths = [];
1068
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1069
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1070
+ matchingPaths.push(microfrontendsJsonPath);
1071
+ }
1072
+ }
1073
+ logger.debug(
1074
+ "[MFE Config] Total matching config files:",
1075
+ matchingPaths.length
1076
+ );
1077
+ if (matchingPaths.length > 1) {
1078
+ throw new MicrofrontendError(
1079
+ `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
1080
+ ${matchingPaths.join("\n \u2022 ")}`,
1081
+ { type: "config", subtype: "inference_failed" }
1082
+ );
1083
+ }
1084
+ if (matchingPaths.length === 0) {
1085
+ if (repositoryRoot && doesMisplacedConfigExist(
1086
+ repositoryRoot,
1087
+ applicationName,
1088
+ customConfigFilename
1089
+ )) {
1090
+ logger.debug(
1091
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1092
+ );
1093
+ const misplacedConfigPath = join2(
1094
+ repositoryRoot,
1095
+ ".vercel",
1096
+ customConfigFilename || "microfrontends.json"
1097
+ );
1098
+ throw new MicrofrontendError(
1099
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1100
+
1101
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1102
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1103
+
1104
+ To fix this:
1105
+ 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
1106
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1107
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1108
+
1109
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1110
+ { type: "config", subtype: "inference_failed" }
1111
+ );
1112
+ }
1113
+ let additionalErrorMessage = "";
1114
+ if (microfrontendsJsonPaths.length > 0) {
1115
+ if (!applicationContext.projectName) {
1116
+ additionalErrorMessage = `
1117
+
1118
+ 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.`;
1119
+ } else {
1120
+ additionalErrorMessage = `
1121
+
1122
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1123
+ }
1124
+ }
1125
+ throw new MicrofrontendError(
1126
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1127
+
1128
+ 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.
1129
+
1130
+ 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.
1131
+
1132
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1133
+ { type: "config", subtype: "inference_failed" }
1134
+ );
1135
+ }
1136
+ const [packageJsonPath] = matchingPaths;
1137
+ return dirname(packageJsonPath);
1138
+ } catch (error2) {
1139
+ if (error2 instanceof MicrofrontendError) {
1140
+ throw error2;
1141
+ }
1142
+ return null;
1143
+ }
1144
+ }
1145
+ function inferMicrofrontendsLocation(opts) {
1146
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
1147
+ if (configCache[cacheKey]) {
1148
+ return configCache[cacheKey];
1149
+ }
1150
+ const result = findPackageWithMicrofrontendsConfig(opts);
1151
+ if (!result) {
1152
+ throw new MicrofrontendError(
1153
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1154
+ { type: "config", subtype: "inference_failed" }
1155
+ );
1156
+ }
1157
+ configCache[cacheKey] = result;
1158
+ return result;
1159
+ }
1160
+ function existsSync(path6) {
1161
+ try {
1162
+ statSync(path6);
1163
+ return true;
1164
+ } catch (_) {
1165
+ return false;
1166
+ }
1167
+ }
1168
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1169
+ logger.debug(
1170
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1171
+ );
1172
+ const misplacedConfigPath = join2(
1173
+ repositoryRoot,
1174
+ ".vercel",
1175
+ customConfigFilename || "microfrontends.json"
1176
+ );
1177
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1178
+ }
1179
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1180
+ try {
1181
+ const microfrontendsJsonContent = readFileSync(
1182
+ microfrontendsJsonPath,
1183
+ "utf-8"
1184
+ );
1185
+ const microfrontendsJson = parse2(microfrontendsJsonContent);
1186
+ if (microfrontendsJson.applications[applicationName]) {
1187
+ logger.debug(
1188
+ "[MFE Config] Found application in config:",
1189
+ microfrontendsJsonPath
1190
+ );
1191
+ return true;
1192
+ }
1193
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1194
+ if (app.packageName === applicationName) {
1195
+ logger.debug(
1196
+ "[MFE Config] Found application via packageName in config:",
1197
+ microfrontendsJsonPath
1198
+ );
1199
+ return true;
1200
+ }
1201
+ }
1202
+ } catch (error2) {
1203
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1204
+ }
1205
+ return false;
1206
+ }
1207
+
1208
+ // src/config/microfrontends/utils/is-monorepo.ts
1209
+ import fs5 from "node:fs";
1210
+ import path4 from "node:path";
1211
+ function isMonorepo({
1212
+ repositoryRoot
1213
+ }) {
1214
+ try {
1215
+ if (fs5.existsSync(path4.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1216
+ return true;
1217
+ }
1218
+ if (fs5.existsSync(path4.join(repositoryRoot, "vlt-workspaces.json"))) {
1219
+ return true;
1220
+ }
1221
+ if (process.env.NX_WORKSPACE_ROOT === path4.resolve(repositoryRoot)) {
1222
+ return true;
1223
+ }
1224
+ const packageJsonPath = path4.join(repositoryRoot, "package.json");
1225
+ if (!fs5.existsSync(packageJsonPath)) {
1226
+ return false;
1227
+ }
1228
+ const packageJson = JSON.parse(
1229
+ fs5.readFileSync(packageJsonPath, "utf-8")
1230
+ );
1231
+ return packageJson.workspaces !== void 0;
1232
+ } catch (error2) {
1233
+ logger.error("Error determining if repository is a monorepo", error2);
1234
+ return false;
1235
+ }
1236
+ }
1237
+
1150
1238
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1151
1239
  import path5 from "node:path";
1152
1240
 
@@ -1160,8 +1248,8 @@ function getOutputFilePath() {
1160
1248
  }
1161
1249
 
1162
1250
  // src/config/microfrontends/server/validation.ts
1163
- import { parse as parse3 } from "jsonc-parser";
1164
1251
  import { Ajv } from "ajv";
1252
+ import { parse as parse3 } from "jsonc-parser";
1165
1253
 
1166
1254
  // schema/schema.json
1167
1255
  var schema_default = {
@@ -1189,9 +1277,7 @@ var schema_default = {
1189
1277
  description: "Optional configuration options for the microfrontend."
1190
1278
  }
1191
1279
  },
1192
- required: [
1193
- "applications"
1194
- ],
1280
+ required: ["applications"],
1195
1281
  additionalProperties: false,
1196
1282
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1197
1283
  },
@@ -1228,19 +1314,14 @@ var schema_default = {
1228
1314
  description: "Development configuration for the default application."
1229
1315
  }
1230
1316
  },
1231
- required: [
1232
- "development"
1233
- ],
1317
+ required: ["development"],
1234
1318
  additionalProperties: false
1235
1319
  },
1236
1320
  DefaultDevelopment: {
1237
1321
  type: "object",
1238
1322
  properties: {
1239
1323
  local: {
1240
- type: [
1241
- "number",
1242
- "string"
1243
- ],
1324
+ type: ["number", "string"],
1244
1325
  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."
1245
1326
  },
1246
1327
  task: {
@@ -1252,9 +1333,7 @@ var schema_default = {
1252
1333
  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."
1253
1334
  }
1254
1335
  },
1255
- required: [
1256
- "fallback"
1257
- ],
1336
+ required: ["fallback"],
1258
1337
  additionalProperties: false
1259
1338
  },
1260
1339
  ChildApplication: {
@@ -1277,19 +1356,14 @@ var schema_default = {
1277
1356
  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."
1278
1357
  }
1279
1358
  },
1280
- required: [
1281
- "routing"
1282
- ],
1359
+ required: ["routing"],
1283
1360
  additionalProperties: false
1284
1361
  },
1285
1362
  ChildDevelopment: {
1286
1363
  type: "object",
1287
1364
  properties: {
1288
1365
  local: {
1289
- type: [
1290
- "number",
1291
- "string"
1292
- ],
1366
+ type: ["number", "string"],
1293
1367
  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
1368
  },
1295
1369
  task: {
@@ -1329,9 +1403,7 @@ var schema_default = {
1329
1403
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1330
1404
  }
1331
1405
  },
1332
- required: [
1333
- "paths"
1334
- ],
1406
+ required: ["paths"],
1335
1407
  additionalProperties: false,
1336
1408
  description: "A group of paths that is routed to this application."
1337
1409
  },
@@ -1574,7 +1646,7 @@ var MicrofrontendsServer = class {
1574
1646
  });
1575
1647
  }
1576
1648
  } else {
1577
- const vercelDir = join2(packageRoot, ".vercel");
1649
+ const vercelDir = join3(packageRoot, ".vercel");
1578
1650
  logger.debug(
1579
1651
  "[MFE Config] Searching for config in .vercel directory:",
1580
1652
  vercelDir