@vercel/microfrontends 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +486 -397
  4. package/dist/config.cjs +193 -171
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +3 -2
  7. package/dist/config.js +194 -172
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +585 -513
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +591 -519
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +607 -535
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +616 -544
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +603 -531
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +609 -537
  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 +725 -649
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +722 -646
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +246 -224
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +247 -225
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +194 -172
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +1 -1
  43. package/dist/next/testing.js +195 -173
  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 +622 -535
  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 +634 -548
  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 +5 -7
@@ -35,40 +35,7 @@ function displayLocalProxyInfo(port) {
35
35
 
36
36
  // src/config/microfrontends/server/index.ts
37
37
  import fs6 from "node:fs";
38
- import { dirname as dirname2, join as join2, resolve } from "node:path";
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));
47
- }
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
- };
57
- }
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;
71
- }
38
+ import { dirname as dirname2, join as join3, resolve } from "node:path";
72
39
 
73
40
  // src/config/errors.ts
74
41
  var MicrofrontendError = class extends Error {
@@ -162,274 +129,47 @@ var MicrofrontendError = class extends Error {
162
129
  }
163
130
  };
164
131
 
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
- import fs from "node:fs";
184
- import path from "node:path";
185
- var GIT_DIRECTORY = ".git";
186
- function hasGitDirectory(dir) {
187
- const gitPath = path.join(dir, GIT_DIRECTORY);
188
- return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
189
- }
190
- function hasPnpmWorkspaces(dir) {
191
- return fs.existsSync(path.join(dir, "pnpm-workspace.yaml"));
192
- }
193
- function hasPackageJson(dir) {
194
- return fs.existsSync(path.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 !== path.parse(currentDir).root) {
203
- if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
204
- return currentDir;
205
- }
206
- if (hasPackageJson(currentDir)) {
207
- lastPackageJsonDir = currentDir;
208
- }
209
- currentDir = path.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
- import { dirname } from "node:path";
221
- import { readFileSync } from "node:fs";
132
+ // src/config/microfrontends-config/isomorphic/index.ts
222
133
  import { parse } from "jsonc-parser";
223
- import fg from "fast-glob";
224
134
 
225
- // src/config/microfrontends/utils/get-config-file-name.ts
226
- var DEFAULT_CONFIGURATION_FILENAMES = [
227
- "microfrontends.json",
228
- "microfrontends.jsonc"
229
- ];
230
- function getPossibleConfigurationFilenames({
231
- customConfigFilename
232
- }) {
233
- if (customConfigFilename) {
234
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
235
- throw new Error(
236
- `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.`
237
- );
238
- }
239
- return Array.from(
240
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
241
- );
242
- }
243
- return DEFAULT_CONFIGURATION_FILENAMES;
244
- }
245
-
246
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
247
- var configCache = {};
248
- function findPackageWithMicrofrontendsConfig({
249
- repositoryRoot,
250
- applicationContext,
251
- customConfigFilename
252
- }) {
253
- const applicationName = applicationContext.name;
254
- logger.debug(
255
- "[MFE Config] Searching repository for configs containing application:",
256
- applicationName
257
- );
258
- try {
259
- const microfrontendsJsonPaths = fg.globSync(
260
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
261
- {
262
- cwd: repositoryRoot,
263
- absolute: true,
264
- onlyFiles: true,
265
- followSymbolicLinks: false,
266
- ignore: ["**/node_modules/**", "**/.git/**"]
267
- }
268
- );
269
- logger.debug(
270
- "[MFE Config] Found",
271
- microfrontendsJsonPaths.length,
272
- "config file(s) in repository"
273
- );
274
- const matchingPaths = [];
275
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
276
- try {
277
- const microfrontendsJsonContent = readFileSync(
278
- microfrontendsJsonPath,
279
- "utf-8"
280
- );
281
- const microfrontendsJson = parse(microfrontendsJsonContent);
282
- if (microfrontendsJson.applications[applicationName]) {
283
- logger.debug(
284
- "[MFE Config] Found application in config:",
285
- microfrontendsJsonPath
286
- );
287
- matchingPaths.push(microfrontendsJsonPath);
288
- } else {
289
- for (const [_, app] of Object.entries(
290
- microfrontendsJson.applications
291
- )) {
292
- if (app.packageName === applicationName) {
293
- logger.debug(
294
- "[MFE Config] Found application via packageName in config:",
295
- microfrontendsJsonPath
296
- );
297
- matchingPaths.push(microfrontendsJsonPath);
298
- }
299
- }
300
- }
301
- } catch (error2) {
302
- }
303
- }
304
- logger.debug(
305
- "[MFE Config] Total matching config files:",
306
- matchingPaths.length
307
- );
308
- if (matchingPaths.length > 1) {
309
- throw new MicrofrontendError(
310
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
311
- ${matchingPaths.join("\n \u2022 ")}`,
312
- { type: "config", subtype: "inference_failed" }
313
- );
314
- }
315
- if (matchingPaths.length === 0) {
316
- let additionalErrorMessage = "";
317
- if (microfrontendsJsonPaths.length > 0) {
318
- if (!applicationContext.projectName) {
319
- additionalErrorMessage = `
320
-
321
- 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.`;
322
- } else {
323
- additionalErrorMessage = `
324
-
325
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
326
- }
327
- }
328
- throw new MicrofrontendError(
329
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
330
-
331
- 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.
332
-
333
- 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.
135
+ // src/config/overrides/constants.ts
136
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
137
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
334
138
 
335
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
336
- { type: "config", subtype: "inference_failed" }
337
- );
338
- }
339
- const [packageJsonPath] = matchingPaths;
340
- return dirname(packageJsonPath);
341
- } catch (error2) {
342
- if (error2 instanceof MicrofrontendError) {
343
- throw error2;
344
- }
345
- return null;
346
- }
347
- }
348
- function inferMicrofrontendsLocation(opts) {
349
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
350
- if (configCache[cacheKey]) {
351
- return configCache[cacheKey];
352
- }
353
- const result = findPackageWithMicrofrontendsConfig(opts);
354
- if (!result) {
355
- throw new MicrofrontendError(
356
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
357
- { type: "config", subtype: "inference_failed" }
358
- );
359
- }
360
- configCache[cacheKey] = result;
361
- return result;
139
+ // src/config/overrides/is-override-cookie.ts
140
+ function isOverrideCookie(cookie) {
141
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
362
142
  }
363
143
 
364
- // src/config/microfrontends/utils/is-monorepo.ts
365
- import fs2 from "node:fs";
366
- import path2 from "node:path";
367
- function isMonorepo({
368
- repositoryRoot
369
- }) {
370
- try {
371
- if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
372
- return true;
373
- }
374
- if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
375
- return true;
376
- }
377
- if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
378
- return true;
379
- }
380
- const packageJsonPath = path2.join(repositoryRoot, "package.json");
381
- if (!fs2.existsSync(packageJsonPath)) {
382
- return false;
383
- }
384
- const packageJson = JSON.parse(
385
- fs2.readFileSync(packageJsonPath, "utf-8")
386
- );
387
- return packageJson.workspaces !== void 0;
388
- } catch (error2) {
389
- logger.error("Error determining if repository is a monorepo", error2);
390
- return false;
391
- }
144
+ // src/config/overrides/get-override-from-cookie.ts
145
+ function getOverrideFromCookie(cookie) {
146
+ if (!isOverrideCookie(cookie) || !cookie.value)
147
+ return;
148
+ return {
149
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
150
+ host: cookie.value
151
+ };
392
152
  }
393
153
 
394
- // src/config/microfrontends/utils/find-package-root.ts
395
- import fs3 from "node:fs";
396
- import path3 from "node:path";
397
- var PACKAGE_JSON = "package.json";
398
- function findPackageRoot(startDir) {
399
- let currentDir = startDir || process.cwd();
400
- while (currentDir !== path3.parse(currentDir).root) {
401
- const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
402
- if (fs3.existsSync(pkgJsonPath)) {
403
- return currentDir;
404
- }
405
- currentDir = path3.dirname(currentDir);
406
- }
407
- throw new Error(
408
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
409
- );
154
+ // src/config/overrides/parse-overrides.ts
155
+ function parseOverrides(cookies) {
156
+ const overridesConfig = { applications: {} };
157
+ cookies.forEach((cookie) => {
158
+ const override = getOverrideFromCookie(cookie);
159
+ if (!override)
160
+ return;
161
+ overridesConfig.applications[override.application] = {
162
+ environment: { host: override.host }
163
+ };
164
+ });
165
+ return overridesConfig;
410
166
  }
411
167
 
412
- // src/config/microfrontends/utils/find-config.ts
413
- import fs4 from "node:fs";
414
- import { join } from "node:path";
415
- function findConfig({
416
- dir,
417
- customConfigFilename
418
- }) {
419
- for (const filename of getPossibleConfigurationFilenames({
420
- customConfigFilename
421
- })) {
422
- const maybeConfig = join(dir, filename);
423
- if (fs4.existsSync(maybeConfig)) {
424
- return maybeConfig;
425
- }
426
- }
427
- return null;
168
+ // src/config/schema/utils/is-default-app.ts
169
+ function isDefaultApp(a) {
170
+ return !("routing" in a);
428
171
  }
429
172
 
430
- // src/config/microfrontends-config/isomorphic/index.ts
431
- import { parse as parse2 } from "jsonc-parser";
432
-
433
173
  // src/config/microfrontends-config/client/index.ts
434
174
  import { pathToRegexp } from "path-to-regexp";
435
175
  var regexpCache = /* @__PURE__ */ new Map();
@@ -528,8 +268,185 @@ var MicrofrontendConfigClient = class {
528
268
  }
529
269
  };
530
270
 
271
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
272
+ function getConfigStringFromEnv() {
273
+ const config = process.env.MFE_CONFIG;
274
+ if (!config) {
275
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
276
+ type: "config",
277
+ subtype: "not_found_in_env"
278
+ });
279
+ }
280
+ return config;
281
+ }
282
+
283
+ // src/config/microfrontends-config/isomorphic/constants.ts
284
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
285
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
286
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
287
+
288
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
289
+ function generatePortFromName({
290
+ name,
291
+ minPort = 3e3,
292
+ maxPort = 8e3
293
+ }) {
294
+ if (!name) {
295
+ throw new Error("Name is required to generate a port");
296
+ }
297
+ let hash = 0;
298
+ for (let i = 0; i < name.length; i++) {
299
+ hash = (hash << 5) - hash + name.charCodeAt(i);
300
+ hash |= 0;
301
+ }
302
+ hash = Math.abs(hash);
303
+ const range = maxPort - minPort;
304
+ const port = minPort + hash % range;
305
+ return port;
306
+ }
307
+
308
+ // src/config/microfrontends-config/isomorphic/host.ts
309
+ var Host = class {
310
+ constructor(hostConfig, options) {
311
+ if (typeof hostConfig === "string") {
312
+ ({
313
+ protocol: this.protocol,
314
+ host: this.host,
315
+ port: this.port
316
+ } = Host.parseUrl(hostConfig));
317
+ } else {
318
+ const { protocol = "https", host, port } = hostConfig;
319
+ this.protocol = protocol;
320
+ this.host = host;
321
+ this.port = port;
322
+ }
323
+ this.local = options?.isLocal;
324
+ }
325
+ static parseUrl(url, defaultProtocol = "https") {
326
+ let hostToParse = url;
327
+ if (!/^https?:\/\//.exec(hostToParse)) {
328
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
329
+ }
330
+ const parsed = new URL(hostToParse);
331
+ if (!parsed.hostname) {
332
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
333
+ }
334
+ if (parsed.hash) {
335
+ throw new Error(
336
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
337
+ );
338
+ }
339
+ if (parsed.username || parsed.password) {
340
+ throw new Error(
341
+ Host.getMicrofrontendsError(
342
+ url,
343
+ "cannot have authentication credentials (username and/or password)"
344
+ )
345
+ );
346
+ }
347
+ if (parsed.pathname !== "/") {
348
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
349
+ }
350
+ if (parsed.search) {
351
+ throw new Error(
352
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
353
+ );
354
+ }
355
+ const protocol = parsed.protocol.slice(0, -1);
356
+ return {
357
+ protocol,
358
+ host: parsed.hostname,
359
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
360
+ };
361
+ }
362
+ static getMicrofrontendsError(url, message) {
363
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
364
+ }
365
+ isLocal() {
366
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
367
+ }
368
+ toString() {
369
+ const url = this.toUrl();
370
+ return url.toString().replace(/\/$/, "");
371
+ }
372
+ toUrl() {
373
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
374
+ return new URL(url);
375
+ }
376
+ };
377
+ var LocalHost = class extends Host {
378
+ constructor({
379
+ appName,
380
+ local
381
+ }) {
382
+ const portOverride = process.env[MFE_APP_PORT_ENV];
383
+ if (portOverride) {
384
+ const overridePort = Number.parseInt(portOverride, 10);
385
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
386
+ super({
387
+ protocol: "http",
388
+ host: "localhost",
389
+ port: overridePort
390
+ });
391
+ return;
392
+ }
393
+ }
394
+ let protocol;
395
+ let host;
396
+ let port;
397
+ if (typeof local === "number") {
398
+ port = local;
399
+ } else if (typeof local === "string") {
400
+ if (/^\d+$/.test(local)) {
401
+ port = Number.parseInt(local, 10);
402
+ } else {
403
+ const parsed = Host.parseUrl(local, "http");
404
+ protocol = parsed.protocol;
405
+ host = parsed.host;
406
+ port = parsed.port;
407
+ }
408
+ } else if (local) {
409
+ protocol = local.protocol;
410
+ host = local.host;
411
+ port = local.port;
412
+ }
413
+ super({
414
+ protocol: protocol ?? "http",
415
+ host: host ?? "localhost",
416
+ port: port ?? generatePortFromName({ name: appName })
417
+ });
418
+ }
419
+ };
420
+
421
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
422
+ import md5 from "md5";
423
+ function hashApplicationName(name) {
424
+ if (!name) {
425
+ throw new Error("Application name is required to generate hash");
426
+ }
427
+ return md5(name).substring(0, 6).padStart(6, "0");
428
+ }
429
+
430
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
431
+ var PREFIX = "vc-ap";
432
+ function generateAssetPrefixFromName({
433
+ name
434
+ }) {
435
+ if (!name) {
436
+ throw new Error("Name is required to generate an asset prefix");
437
+ }
438
+ return `${PREFIX}-${hashApplicationName(name)}`;
439
+ }
440
+
441
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
442
+ function generateAutomationBypassEnvVarName({
443
+ name
444
+ }) {
445
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
446
+ }
447
+
531
448
  // src/config/microfrontends-config/isomorphic/validation.ts
532
- import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
449
+ import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
533
450
  var LIST_FORMATTER = new Intl.ListFormat("en", {
534
451
  style: "long",
535
452
  type: "conjunction"
@@ -601,7 +518,7 @@ var validateConfigPaths = (applicationConfigsById) => {
601
518
  );
602
519
  }
603
520
  };
604
- var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
521
+ var PATH_DEFAULT_PATTERNS = ["[^\\/#\\?]+?", "(?:(?!\\.)[^\\/#\\?])+?"];
605
522
  function validatePathExpression(path6) {
606
523
  try {
607
524
  const tokens = parsePathRegexp(path6);
@@ -623,7 +540,7 @@ function validatePathExpression(path6) {
623
540
  if (!token.name) {
624
541
  return `Only named wildcards are allowed: ${path6} (hint: add ":path" to the wildcard)`;
625
542
  }
626
- if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
543
+ if (!PATH_DEFAULT_PATTERNS.includes(token.pattern) && // Allows (a|b|c) and ((?!a|b|c).*) regex
627
544
  // Only limited regex is supported for now, due to performance considerations
628
545
  // Allows all letters, numbers, and hyphens. Other characters must be escaped.
629
546
  !/^(?<allowed>[\w-~]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w-~]+(?:\|[^:|()]+)*)\)\.\*$/.test(
@@ -709,154 +626,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
709
626
  }
710
627
  };
711
628
 
712
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
713
- import md5 from "md5";
714
- function hashApplicationName(name) {
715
- if (!name) {
716
- throw new Error("Application name is required to generate hash");
717
- }
718
- return md5(name).substring(0, 6).padStart(6, "0");
719
- }
720
-
721
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
722
- var PREFIX = "vc-ap";
723
- function generateAssetPrefixFromName({
724
- name
725
- }) {
726
- if (!name) {
727
- throw new Error("Name is required to generate an asset prefix");
728
- }
729
- return `${PREFIX}-${hashApplicationName(name)}`;
730
- }
731
-
732
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
733
- function generatePortFromName({
734
- name,
735
- minPort = 3e3,
736
- maxPort = 8e3
737
- }) {
738
- if (!name) {
739
- throw new Error("Name is required to generate a port");
740
- }
741
- let hash = 0;
742
- for (let i = 0; i < name.length; i++) {
743
- hash = (hash << 5) - hash + name.charCodeAt(i);
744
- hash |= 0;
745
- }
746
- hash = Math.abs(hash);
747
- const range = maxPort - minPort;
748
- const port = minPort + hash % range;
749
- return port;
750
- }
751
-
752
- // src/config/microfrontends-config/isomorphic/host.ts
753
- var Host = class {
754
- constructor(hostConfig, options) {
755
- if (typeof hostConfig === "string") {
756
- ({
757
- protocol: this.protocol,
758
- host: this.host,
759
- port: this.port
760
- } = Host.parseUrl(hostConfig));
761
- } else {
762
- const { protocol = "https", host, port } = hostConfig;
763
- this.protocol = protocol;
764
- this.host = host;
765
- this.port = port;
766
- }
767
- this.local = options?.isLocal;
768
- }
769
- static parseUrl(url, defaultProtocol = "https") {
770
- let hostToParse = url;
771
- if (!/^https?:\/\//.exec(hostToParse)) {
772
- hostToParse = `${defaultProtocol}://${hostToParse}`;
773
- }
774
- const parsed = new URL(hostToParse);
775
- if (!parsed.hostname) {
776
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
777
- }
778
- if (parsed.hash) {
779
- throw new Error(
780
- Host.getMicrofrontendsError(url, "cannot have a fragment")
781
- );
782
- }
783
- if (parsed.username || parsed.password) {
784
- throw new Error(
785
- Host.getMicrofrontendsError(
786
- url,
787
- "cannot have authentication credentials (username and/or password)"
788
- )
789
- );
790
- }
791
- if (parsed.pathname !== "/") {
792
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
793
- }
794
- if (parsed.search) {
795
- throw new Error(
796
- Host.getMicrofrontendsError(url, "cannot have query parameters")
797
- );
798
- }
799
- const protocol = parsed.protocol.slice(0, -1);
800
- return {
801
- protocol,
802
- host: parsed.hostname,
803
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
804
- };
805
- }
806
- static getMicrofrontendsError(url, message) {
807
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
808
- }
809
- isLocal() {
810
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
811
- }
812
- toString() {
813
- const url = this.toUrl();
814
- return url.toString().replace(/\/$/, "");
815
- }
816
- toUrl() {
817
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
818
- return new URL(url);
819
- }
820
- };
821
- var LocalHost = class extends Host {
822
- constructor({
823
- appName,
824
- local
825
- }) {
826
- let protocol;
827
- let host;
828
- let port;
829
- if (typeof local === "number") {
830
- port = local;
831
- } else if (typeof local === "string") {
832
- if (/^\d+$/.test(local)) {
833
- port = Number.parseInt(local);
834
- } else {
835
- const parsed = Host.parseUrl(local, "http");
836
- protocol = parsed.protocol;
837
- host = parsed.host;
838
- port = parsed.port;
839
- }
840
- } else if (local) {
841
- protocol = local.protocol;
842
- host = local.host;
843
- port = local.port;
844
- }
845
- super({
846
- protocol: protocol ?? "http",
847
- host: host ?? "localhost",
848
- port: port ?? generatePortFromName({ name: appName })
849
- });
850
- }
851
- };
852
-
853
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
854
- function generateAutomationBypassEnvVarName({
855
- name
856
- }) {
857
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
858
- }
859
-
860
629
  // src/config/microfrontends-config/isomorphic/application.ts
861
630
  var Application = class {
862
631
  constructor(name, {
@@ -937,9 +706,6 @@ var ChildApplication = class extends Application {
937
706
  }
938
707
  };
939
708
 
940
- // src/config/microfrontends-config/isomorphic/constants.ts
941
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
942
-
943
709
  // src/config/microfrontends-config/isomorphic/index.ts
944
710
  var MicrofrontendConfigIsomorphic = class {
945
711
  constructor({
@@ -983,7 +749,7 @@ var MicrofrontendConfigIsomorphic = class {
983
749
  };
984
750
  }
985
751
  static validate(config) {
986
- const c = typeof config === "string" ? parse2(config) : config;
752
+ const c = typeof config === "string" ? parse(config) : config;
987
753
  validateConfigPaths(c.applications);
988
754
  validateConfigDefaultApplication(c.applications);
989
755
  return c;
@@ -992,7 +758,7 @@ var MicrofrontendConfigIsomorphic = class {
992
758
  cookies
993
759
  }) {
994
760
  return new MicrofrontendConfigIsomorphic({
995
- config: parse2(getConfigStringFromEnv()),
761
+ config: parse(getConfigStringFromEnv()),
996
762
  overrides: parseOverrides(cookies ?? [])
997
763
  });
998
764
  }
@@ -1057,77 +823,193 @@ var MicrofrontendConfigIsomorphic = class {
1057
823
  getDefaultApplication() {
1058
824
  return this.defaultApplication;
1059
825
  }
1060
- /**
1061
- * Returns the configured port for the local proxy
1062
- */
1063
- getLocalProxyPort() {
1064
- return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
826
+ /**
827
+ * Returns the configured port for the local proxy.
828
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
829
+ */
830
+ getLocalProxyPort() {
831
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
832
+ if (portOverride) {
833
+ const port = Number.parseInt(portOverride, 10);
834
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
835
+ return port;
836
+ }
837
+ }
838
+ return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
839
+ }
840
+ toClientConfig(options) {
841
+ const applications = Object.fromEntries(
842
+ Object.entries(this.childApplications).map(([name, application]) => [
843
+ hashApplicationName(name),
844
+ {
845
+ default: false,
846
+ routing: application.routing
847
+ }
848
+ ])
849
+ );
850
+ applications[hashApplicationName(this.defaultApplication.name)] = {
851
+ default: true
852
+ };
853
+ return new MicrofrontendConfigClient(
854
+ {
855
+ applications
856
+ },
857
+ {
858
+ removeFlaggedPaths: options?.removeFlaggedPaths
859
+ }
860
+ );
861
+ }
862
+ /**
863
+ * Serializes the class back to the Schema type.
864
+ *
865
+ * NOTE: This is used when writing the config to disk and must always match the input Schema
866
+ */
867
+ toSchemaJson() {
868
+ return this.serialized.config;
869
+ }
870
+ serialize() {
871
+ return this.serialized;
872
+ }
873
+ };
874
+
875
+ // src/config/microfrontends/utils/find-config.ts
876
+ import fs from "node:fs";
877
+ import { join } from "node:path";
878
+
879
+ // src/config/microfrontends/utils/get-config-file-name.ts
880
+ var DEFAULT_CONFIGURATION_FILENAMES = [
881
+ "microfrontends.json",
882
+ "microfrontends.jsonc"
883
+ ];
884
+ function getPossibleConfigurationFilenames({
885
+ customConfigFilename
886
+ }) {
887
+ if (customConfigFilename) {
888
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
889
+ throw new Error(
890
+ `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.`
891
+ );
892
+ }
893
+ return Array.from(
894
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
895
+ );
896
+ }
897
+ return DEFAULT_CONFIGURATION_FILENAMES;
898
+ }
899
+
900
+ // src/config/microfrontends/utils/find-config.ts
901
+ function findConfig({
902
+ dir,
903
+ customConfigFilename
904
+ }) {
905
+ for (const filename of getPossibleConfigurationFilenames({
906
+ customConfigFilename
907
+ })) {
908
+ const maybeConfig = join(dir, filename);
909
+ if (fs.existsSync(maybeConfig)) {
910
+ return maybeConfig;
911
+ }
912
+ }
913
+ return null;
914
+ }
915
+
916
+ // src/config/microfrontends/utils/find-package-root.ts
917
+ import fs2 from "node:fs";
918
+ import path from "node:path";
919
+ var PACKAGE_JSON = "package.json";
920
+ function findPackageRoot(startDir) {
921
+ let currentDir = startDir || process.cwd();
922
+ while (currentDir !== path.parse(currentDir).root) {
923
+ const pkgJsonPath = path.join(currentDir, PACKAGE_JSON);
924
+ if (fs2.existsSync(pkgJsonPath)) {
925
+ return currentDir;
926
+ }
927
+ currentDir = path.dirname(currentDir);
1065
928
  }
1066
- toClientConfig(options) {
1067
- const applications = Object.fromEntries(
1068
- Object.entries(this.childApplications).map(([name, application]) => [
1069
- hashApplicationName(name),
1070
- {
1071
- default: false,
1072
- routing: application.routing
1073
- }
1074
- ])
1075
- );
1076
- applications[hashApplicationName(this.defaultApplication.name)] = {
1077
- default: true
1078
- };
1079
- return new MicrofrontendConfigClient(
1080
- {
1081
- applications
1082
- },
1083
- {
1084
- removeFlaggedPaths: options?.removeFlaggedPaths
1085
- }
1086
- );
929
+ throw new Error(
930
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
931
+ );
932
+ }
933
+
934
+ // src/config/microfrontends/utils/find-repository-root.ts
935
+ import fs3 from "node:fs";
936
+ import path2 from "node:path";
937
+ var GIT_DIRECTORY = ".git";
938
+ function hasGitDirectory(dir) {
939
+ const gitPath = path2.join(dir, GIT_DIRECTORY);
940
+ return fs3.existsSync(gitPath) && fs3.statSync(gitPath).isDirectory();
941
+ }
942
+ function hasPnpmWorkspaces(dir) {
943
+ return fs3.existsSync(path2.join(dir, "pnpm-workspace.yaml"));
944
+ }
945
+ function hasPackageJson(dir) {
946
+ return fs3.existsSync(path2.join(dir, "package.json"));
947
+ }
948
+ function findRepositoryRoot(startDir) {
949
+ if (process.env.NX_WORKSPACE_ROOT) {
950
+ return process.env.NX_WORKSPACE_ROOT;
1087
951
  }
1088
- /**
1089
- * Serializes the class back to the Schema type.
1090
- *
1091
- * NOTE: This is used when writing the config to disk and must always match the input Schema
1092
- */
1093
- toSchemaJson() {
1094
- return this.serialized.config;
952
+ let currentDir = startDir || process.cwd();
953
+ let lastPackageJsonDir = null;
954
+ while (currentDir !== path2.parse(currentDir).root) {
955
+ if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
956
+ return currentDir;
957
+ }
958
+ if (hasPackageJson(currentDir)) {
959
+ lastPackageJsonDir = currentDir;
960
+ }
961
+ currentDir = path2.dirname(currentDir);
1095
962
  }
1096
- serialize() {
1097
- return this.serialized;
963
+ if (lastPackageJsonDir) {
964
+ return lastPackageJsonDir;
1098
965
  }
1099
- };
966
+ throw new Error(
967
+ `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.`
968
+ );
969
+ }
1100
970
 
1101
971
  // src/config/microfrontends/utils/get-application-context.ts
1102
- import fs5 from "node:fs";
1103
- import path4 from "node:path";
972
+ import fs4 from "node:fs";
973
+ import path3 from "node:path";
1104
974
  function getApplicationContext(opts) {
1105
975
  if (opts?.appName) {
1106
- logger.debug("[MFE Config] Application name from appName parameter:", opts.appName);
976
+ logger.debug(
977
+ "[MFE Config] Application name from appName parameter:",
978
+ opts.appName
979
+ );
1107
980
  return { name: opts.appName };
1108
981
  }
1109
982
  if (process.env.VERCEL_PROJECT_NAME) {
1110
- logger.debug("[MFE Config] Application name from VERCEL_PROJECT_NAME:", process.env.VERCEL_PROJECT_NAME);
983
+ logger.debug(
984
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
985
+ process.env.VERCEL_PROJECT_NAME
986
+ );
1111
987
  return {
1112
988
  name: process.env.VERCEL_PROJECT_NAME,
1113
989
  projectName: process.env.VERCEL_PROJECT_NAME
1114
990
  };
1115
991
  }
1116
992
  if (process.env.NX_TASK_TARGET_PROJECT) {
1117
- logger.debug("[MFE Config] Application name from NX_TASK_TARGET_PROJECT:", process.env.NX_TASK_TARGET_PROJECT);
993
+ logger.debug(
994
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
995
+ process.env.NX_TASK_TARGET_PROJECT
996
+ );
1118
997
  return {
1119
998
  name: process.env.NX_TASK_TARGET_PROJECT,
1120
999
  packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1121
1000
  };
1122
1001
  }
1123
1002
  try {
1124
- const vercelProjectJsonPath = fs5.readFileSync(
1125
- path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1003
+ const vercelProjectJsonPath = fs4.readFileSync(
1004
+ path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1126
1005
  "utf-8"
1127
1006
  );
1128
1007
  const projectJson = JSON.parse(vercelProjectJsonPath);
1129
1008
  if (projectJson.projectName) {
1130
- logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
1009
+ logger.debug(
1010
+ "[MFE Config] Application name from .vercel/project.json:",
1011
+ projectJson.projectName
1012
+ );
1131
1013
  return {
1132
1014
  name: projectJson.projectName,
1133
1015
  projectName: projectJson.projectName
@@ -1136,8 +1018,8 @@ function getApplicationContext(opts) {
1136
1018
  } catch (_) {
1137
1019
  }
1138
1020
  try {
1139
- const packageJsonString = fs5.readFileSync(
1140
- path4.join(opts?.packageRoot || ".", "package.json"),
1021
+ const packageJsonString = fs4.readFileSync(
1022
+ path3.join(opts?.packageRoot || ".", "package.json"),
1141
1023
  "utf-8"
1142
1024
  );
1143
1025
  const packageJson = JSON.parse(packageJsonString);
@@ -1151,7 +1033,10 @@ function getApplicationContext(opts) {
1151
1033
  }
1152
1034
  );
1153
1035
  }
1154
- logger.debug("[MFE Config] Application name from package.json:", packageJson.name);
1036
+ logger.debug(
1037
+ "[MFE Config] Application name from package.json:",
1038
+ packageJson.name
1039
+ );
1155
1040
  return { name: packageJson.name, packageJsonName: packageJson.name };
1156
1041
  } catch (err) {
1157
1042
  throw MicrofrontendError.handle(err, {
@@ -1160,6 +1045,209 @@ function getApplicationContext(opts) {
1160
1045
  }
1161
1046
  }
1162
1047
 
1048
+ // src/config/microfrontends/utils/infer-microfrontends-location.ts
1049
+ import { readFileSync, statSync } from "node:fs";
1050
+ import { dirname, join as join2 } from "node:path";
1051
+ import fg from "fast-glob";
1052
+ import { parse as parse2 } from "jsonc-parser";
1053
+ var configCache = {};
1054
+ function findPackageWithMicrofrontendsConfig({
1055
+ repositoryRoot,
1056
+ applicationContext,
1057
+ customConfigFilename
1058
+ }) {
1059
+ const applicationName = applicationContext.name;
1060
+ logger.debug(
1061
+ "[MFE Config] Searching repository for configs containing application:",
1062
+ applicationName
1063
+ );
1064
+ try {
1065
+ const microfrontendsJsonPaths = fg.globSync(
1066
+ `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
1067
+ {
1068
+ cwd: repositoryRoot,
1069
+ absolute: true,
1070
+ onlyFiles: true,
1071
+ followSymbolicLinks: false,
1072
+ ignore: ["**/node_modules/**", "**/.git/**"]
1073
+ }
1074
+ );
1075
+ logger.debug(
1076
+ "[MFE Config] Found",
1077
+ microfrontendsJsonPaths.length,
1078
+ "config file(s) in repository"
1079
+ );
1080
+ const matchingPaths = [];
1081
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1082
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1083
+ matchingPaths.push(microfrontendsJsonPath);
1084
+ }
1085
+ }
1086
+ logger.debug(
1087
+ "[MFE Config] Total matching config files:",
1088
+ matchingPaths.length
1089
+ );
1090
+ if (matchingPaths.length > 1) {
1091
+ throw new MicrofrontendError(
1092
+ `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
1093
+ ${matchingPaths.join("\n \u2022 ")}`,
1094
+ { type: "config", subtype: "inference_failed" }
1095
+ );
1096
+ }
1097
+ if (matchingPaths.length === 0) {
1098
+ if (repositoryRoot && doesMisplacedConfigExist(
1099
+ repositoryRoot,
1100
+ applicationName,
1101
+ customConfigFilename
1102
+ )) {
1103
+ logger.debug(
1104
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1105
+ );
1106
+ const misplacedConfigPath = join2(
1107
+ repositoryRoot,
1108
+ ".vercel",
1109
+ customConfigFilename || "microfrontends.json"
1110
+ );
1111
+ throw new MicrofrontendError(
1112
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1113
+
1114
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1115
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1116
+
1117
+ To fix this:
1118
+ 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
1119
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1120
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1121
+
1122
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1123
+ { type: "config", subtype: "inference_failed" }
1124
+ );
1125
+ }
1126
+ let additionalErrorMessage = "";
1127
+ if (microfrontendsJsonPaths.length > 0) {
1128
+ if (!applicationContext.projectName) {
1129
+ additionalErrorMessage = `
1130
+
1131
+ 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.`;
1132
+ } else {
1133
+ additionalErrorMessage = `
1134
+
1135
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1136
+ }
1137
+ }
1138
+ throw new MicrofrontendError(
1139
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1140
+
1141
+ 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.
1142
+
1143
+ 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.
1144
+
1145
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1146
+ { type: "config", subtype: "inference_failed" }
1147
+ );
1148
+ }
1149
+ const [packageJsonPath] = matchingPaths;
1150
+ return dirname(packageJsonPath);
1151
+ } catch (error2) {
1152
+ if (error2 instanceof MicrofrontendError) {
1153
+ throw error2;
1154
+ }
1155
+ return null;
1156
+ }
1157
+ }
1158
+ function inferMicrofrontendsLocation(opts) {
1159
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
1160
+ if (configCache[cacheKey]) {
1161
+ return configCache[cacheKey];
1162
+ }
1163
+ const result = findPackageWithMicrofrontendsConfig(opts);
1164
+ if (!result) {
1165
+ throw new MicrofrontendError(
1166
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1167
+ { type: "config", subtype: "inference_failed" }
1168
+ );
1169
+ }
1170
+ configCache[cacheKey] = result;
1171
+ return result;
1172
+ }
1173
+ function existsSync(path6) {
1174
+ try {
1175
+ statSync(path6);
1176
+ return true;
1177
+ } catch (_) {
1178
+ return false;
1179
+ }
1180
+ }
1181
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1182
+ logger.debug(
1183
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1184
+ );
1185
+ const misplacedConfigPath = join2(
1186
+ repositoryRoot,
1187
+ ".vercel",
1188
+ customConfigFilename || "microfrontends.json"
1189
+ );
1190
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1191
+ }
1192
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1193
+ try {
1194
+ const microfrontendsJsonContent = readFileSync(
1195
+ microfrontendsJsonPath,
1196
+ "utf-8"
1197
+ );
1198
+ const microfrontendsJson = parse2(microfrontendsJsonContent);
1199
+ if (microfrontendsJson.applications[applicationName]) {
1200
+ logger.debug(
1201
+ "[MFE Config] Found application in config:",
1202
+ microfrontendsJsonPath
1203
+ );
1204
+ return true;
1205
+ }
1206
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1207
+ if (app.packageName === applicationName) {
1208
+ logger.debug(
1209
+ "[MFE Config] Found application via packageName in config:",
1210
+ microfrontendsJsonPath
1211
+ );
1212
+ return true;
1213
+ }
1214
+ }
1215
+ } catch (error2) {
1216
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1217
+ }
1218
+ return false;
1219
+ }
1220
+
1221
+ // src/config/microfrontends/utils/is-monorepo.ts
1222
+ import fs5 from "node:fs";
1223
+ import path4 from "node:path";
1224
+ function isMonorepo({
1225
+ repositoryRoot
1226
+ }) {
1227
+ try {
1228
+ if (fs5.existsSync(path4.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1229
+ return true;
1230
+ }
1231
+ if (fs5.existsSync(path4.join(repositoryRoot, "vlt-workspaces.json"))) {
1232
+ return true;
1233
+ }
1234
+ if (process.env.NX_WORKSPACE_ROOT === path4.resolve(repositoryRoot)) {
1235
+ return true;
1236
+ }
1237
+ const packageJsonPath = path4.join(repositoryRoot, "package.json");
1238
+ if (!fs5.existsSync(packageJsonPath)) {
1239
+ return false;
1240
+ }
1241
+ const packageJson = JSON.parse(
1242
+ fs5.readFileSync(packageJsonPath, "utf-8")
1243
+ );
1244
+ return packageJson.workspaces !== void 0;
1245
+ } catch (error2) {
1246
+ logger.error("Error determining if repository is a monorepo", error2);
1247
+ return false;
1248
+ }
1249
+ }
1250
+
1163
1251
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1164
1252
  import path5 from "node:path";
1165
1253
 
@@ -1173,8 +1261,8 @@ function getOutputFilePath() {
1173
1261
  }
1174
1262
 
1175
1263
  // src/config/microfrontends/server/validation.ts
1176
- import { parse as parse3 } from "jsonc-parser";
1177
1264
  import { Ajv } from "ajv";
1265
+ import { parse as parse3 } from "jsonc-parser";
1178
1266
 
1179
1267
  // schema/schema.json
1180
1268
  var schema_default = {
@@ -1202,9 +1290,7 @@ var schema_default = {
1202
1290
  description: "Optional configuration options for the microfrontend."
1203
1291
  }
1204
1292
  },
1205
- required: [
1206
- "applications"
1207
- ],
1293
+ required: ["applications"],
1208
1294
  additionalProperties: false,
1209
1295
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1210
1296
  },
@@ -1241,19 +1327,14 @@ var schema_default = {
1241
1327
  description: "Development configuration for the default application."
1242
1328
  }
1243
1329
  },
1244
- required: [
1245
- "development"
1246
- ],
1330
+ required: ["development"],
1247
1331
  additionalProperties: false
1248
1332
  },
1249
1333
  DefaultDevelopment: {
1250
1334
  type: "object",
1251
1335
  properties: {
1252
1336
  local: {
1253
- type: [
1254
- "number",
1255
- "string"
1256
- ],
1337
+ type: ["number", "string"],
1257
1338
  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."
1258
1339
  },
1259
1340
  task: {
@@ -1265,9 +1346,7 @@ var schema_default = {
1265
1346
  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."
1266
1347
  }
1267
1348
  },
1268
- required: [
1269
- "fallback"
1270
- ],
1349
+ required: ["fallback"],
1271
1350
  additionalProperties: false
1272
1351
  },
1273
1352
  ChildApplication: {
@@ -1290,19 +1369,14 @@ var schema_default = {
1290
1369
  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."
1291
1370
  }
1292
1371
  },
1293
- required: [
1294
- "routing"
1295
- ],
1372
+ required: ["routing"],
1296
1373
  additionalProperties: false
1297
1374
  },
1298
1375
  ChildDevelopment: {
1299
1376
  type: "object",
1300
1377
  properties: {
1301
1378
  local: {
1302
- type: [
1303
- "number",
1304
- "string"
1305
- ],
1379
+ type: ["number", "string"],
1306
1380
  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."
1307
1381
  },
1308
1382
  task: {
@@ -1342,9 +1416,7 @@ var schema_default = {
1342
1416
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1343
1417
  }
1344
1418
  },
1345
- required: [
1346
- "paths"
1347
- ],
1419
+ required: ["paths"],
1348
1420
  additionalProperties: false,
1349
1421
  description: "A group of paths that is routed to this application."
1350
1422
  },
@@ -1587,7 +1659,7 @@ var MicrofrontendsServer = class {
1587
1659
  });
1588
1660
  }
1589
1661
  } else {
1590
- const vercelDir = join2(packageRoot, ".vercel");
1662
+ const vercelDir = join3(packageRoot, ".vercel");
1591
1663
  logger.debug(
1592
1664
  "[MFE Config] Searching for config in .vercel directory:",
1593
1665
  vercelDir