@vercel/microfrontends 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +551 -381
  4. package/dist/config.cjs +191 -169
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +3 -2
  7. package/dist/config.js +192 -170
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +643 -489
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +645 -491
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +671 -519
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +669 -517
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +661 -509
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +663 -511
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +117 -23
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +4 -4
  25. package/dist/microfrontends/utils.js +118 -24
  26. package/dist/microfrontends/utils.js.map +1 -1
  27. package/dist/next/client.cjs +1 -1
  28. package/dist/next/client.cjs.map +1 -1
  29. package/dist/next/client.d.ts +8 -8
  30. package/dist/next/client.js +1 -1
  31. package/dist/next/client.js.map +1 -1
  32. package/dist/next/config.cjs +784 -626
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +782 -624
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +244 -222
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +245 -223
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +192 -170
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +1 -1
  43. package/dist/next/testing.js +193 -171
  44. package/dist/next/testing.js.map +1 -1
  45. package/dist/overrides.cjs +5 -5
  46. package/dist/overrides.cjs.map +1 -1
  47. package/dist/overrides.d.ts +9 -9
  48. package/dist/overrides.js +5 -5
  49. package/dist/overrides.js.map +1 -1
  50. package/dist/utils/mfe-port.cjs +689 -522
  51. package/dist/utils/mfe-port.cjs.map +1 -1
  52. package/dist/utils/mfe-port.d.ts +9 -1
  53. package/dist/utils/mfe-port.js +687 -521
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +8 -24
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.js +8 -24
  58. package/dist/validation.js.map +1 -1
  59. package/package.json +4 -6
@@ -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,253 +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
- try {
255
- const microfrontendsJsonPaths = fg.globSync(
256
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
257
- {
258
- cwd: repositoryRoot,
259
- absolute: true,
260
- onlyFiles: true,
261
- followSymbolicLinks: false,
262
- ignore: ["**/node_modules/**", "**/.git/**"]
263
- }
264
- );
265
- const matchingPaths = [];
266
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
267
- try {
268
- const microfrontendsJsonContent = readFileSync(
269
- microfrontendsJsonPath,
270
- "utf-8"
271
- );
272
- const microfrontendsJson = parse(microfrontendsJsonContent);
273
- if (microfrontendsJson.applications[applicationName]) {
274
- matchingPaths.push(microfrontendsJsonPath);
275
- } else {
276
- for (const [_, app] of Object.entries(
277
- microfrontendsJson.applications
278
- )) {
279
- if (app.packageName === applicationName) {
280
- matchingPaths.push(microfrontendsJsonPath);
281
- }
282
- }
283
- }
284
- } catch (error2) {
285
- }
286
- }
287
- if (matchingPaths.length > 1) {
288
- throw new MicrofrontendError(
289
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
290
- ${matchingPaths.join("\n \u2022 ")}`,
291
- { type: "config", subtype: "inference_failed" }
292
- );
293
- }
294
- if (matchingPaths.length === 0) {
295
- let additionalErrorMessage = "";
296
- if (microfrontendsJsonPaths.length > 0) {
297
- if (!applicationContext.projectName) {
298
- additionalErrorMessage = `
299
-
300
- 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.`;
301
- } else {
302
- additionalErrorMessage = `
303
-
304
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
305
- }
306
- }
307
- throw new MicrofrontendError(
308
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
309
-
310
- 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.
311
-
312
- 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:`;
313
138
 
314
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
315
- { type: "config", subtype: "inference_failed" }
316
- );
317
- }
318
- const [packageJsonPath] = matchingPaths;
319
- return dirname(packageJsonPath);
320
- } catch (error2) {
321
- if (error2 instanceof MicrofrontendError) {
322
- throw error2;
323
- }
324
- return null;
325
- }
326
- }
327
- function inferMicrofrontendsLocation(opts) {
328
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
329
- if (configCache[cacheKey]) {
330
- return configCache[cacheKey];
331
- }
332
- const result = findPackageWithMicrofrontendsConfig(opts);
333
- if (!result) {
334
- throw new MicrofrontendError(
335
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
336
- { type: "config", subtype: "inference_failed" }
337
- );
338
- }
339
- configCache[cacheKey] = result;
340
- return result;
139
+ // src/config/overrides/is-override-cookie.ts
140
+ function isOverrideCookie(cookie) {
141
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
341
142
  }
342
143
 
343
- // src/config/microfrontends/utils/is-monorepo.ts
344
- import fs2 from "node:fs";
345
- import path2 from "node:path";
346
- function isMonorepo({
347
- repositoryRoot
348
- }) {
349
- try {
350
- if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
351
- return true;
352
- }
353
- if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
354
- return true;
355
- }
356
- if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
357
- return true;
358
- }
359
- const packageJsonPath = path2.join(repositoryRoot, "package.json");
360
- if (!fs2.existsSync(packageJsonPath)) {
361
- return false;
362
- }
363
- const packageJson = JSON.parse(
364
- fs2.readFileSync(packageJsonPath, "utf-8")
365
- );
366
- return packageJson.workspaces !== void 0;
367
- } catch (error2) {
368
- logger.error("Error determining if repository is a monorepo", error2);
369
- return false;
370
- }
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
+ };
371
152
  }
372
153
 
373
- // src/config/microfrontends/utils/find-package-root.ts
374
- import fs3 from "node:fs";
375
- import path3 from "node:path";
376
- var PACKAGE_JSON = "package.json";
377
- function findPackageRoot(startDir) {
378
- let currentDir = startDir || process.cwd();
379
- while (currentDir !== path3.parse(currentDir).root) {
380
- const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
381
- if (fs3.existsSync(pkgJsonPath)) {
382
- return currentDir;
383
- }
384
- currentDir = path3.dirname(currentDir);
385
- }
386
- throw new Error(
387
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
388
- );
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;
389
166
  }
390
167
 
391
- // src/config/microfrontends/utils/find-config.ts
392
- import fs4 from "node:fs";
393
- import { join } from "node:path";
394
- function findConfig({
395
- dir,
396
- customConfigFilename
397
- }) {
398
- for (const filename of getPossibleConfigurationFilenames({
399
- customConfigFilename
400
- })) {
401
- const maybeConfig = join(dir, filename);
402
- if (fs4.existsSync(maybeConfig)) {
403
- return maybeConfig;
404
- }
405
- }
406
- return null;
168
+ // src/config/schema/utils/is-default-app.ts
169
+ function isDefaultApp(a) {
170
+ return !("routing" in a);
407
171
  }
408
172
 
409
- // src/config/microfrontends-config/isomorphic/index.ts
410
- import { parse as parse2 } from "jsonc-parser";
411
-
412
173
  // src/config/microfrontends-config/client/index.ts
413
174
  import { pathToRegexp } from "path-to-regexp";
414
175
  var regexpCache = /* @__PURE__ */ new Map();
@@ -467,48 +228,225 @@ var MicrofrontendConfigClient = class {
467
228
  "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
468
229
  );
469
230
  }
470
- return new MicrofrontendConfigClient(JSON.parse(config));
231
+ return new MicrofrontendConfigClient(JSON.parse(config));
232
+ }
233
+ isEqual(other) {
234
+ return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
235
+ }
236
+ getApplicationNameForPath(path6) {
237
+ if (!path6.startsWith("/")) {
238
+ throw new Error(`Path must start with a /`);
239
+ }
240
+ if (this.pathCache[path6]) {
241
+ return this.pathCache[path6];
242
+ }
243
+ const pathname = new URL(path6, "https://example.com").pathname;
244
+ for (const [name, application] of Object.entries(this.applications)) {
245
+ if (application.routing) {
246
+ for (const group of application.routing) {
247
+ for (const childPath of group.paths) {
248
+ const regexp = getRegexp(childPath);
249
+ if (regexp.test(pathname)) {
250
+ this.pathCache[path6] = name;
251
+ return name;
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+ const defaultApplication = Object.entries(this.applications).find(
258
+ ([, application]) => application.default
259
+ );
260
+ if (!defaultApplication) {
261
+ return null;
262
+ }
263
+ this.pathCache[path6] = defaultApplication[0];
264
+ return defaultApplication[0];
265
+ }
266
+ serialize() {
267
+ return this.serialized;
268
+ }
269
+ };
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
+ };
471
361
  }
472
- isEqual(other) {
473
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
362
+ static getMicrofrontendsError(url, message) {
363
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
474
364
  }
475
- getApplicationNameForPath(path6) {
476
- if (!path6.startsWith("/")) {
477
- throw new Error(`Path must start with a /`);
478
- }
479
- if (this.pathCache[path6]) {
480
- return this.pathCache[path6];
481
- }
482
- const pathname = new URL(path6, "https://example.com").pathname;
483
- for (const [name, application] of Object.entries(this.applications)) {
484
- if (application.routing) {
485
- for (const group of application.routing) {
486
- for (const childPath of group.paths) {
487
- const regexp = getRegexp(childPath);
488
- if (regexp.test(pathname)) {
489
- this.pathCache[path6] = name;
490
- return name;
491
- }
492
- }
493
- }
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;
494
392
  }
495
393
  }
496
- const defaultApplication = Object.entries(this.applications).find(
497
- ([, application]) => application.default
498
- );
499
- if (!defaultApplication) {
500
- return null;
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;
501
412
  }
502
- this.pathCache[path6] = defaultApplication[0];
503
- return defaultApplication[0];
504
- }
505
- serialize() {
506
- return this.serialized;
413
+ super({
414
+ protocol: protocol ?? "http",
415
+ host: host ?? "localhost",
416
+ port: port ?? generatePortFromName({ name: appName })
417
+ });
507
418
  }
508
419
  };
509
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
+
510
448
  // src/config/microfrontends-config/isomorphic/validation.ts
511
- import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
449
+ import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
512
450
  var LIST_FORMATTER = new Intl.ListFormat("en", {
513
451
  style: "long",
514
452
  type: "conjunction"
@@ -684,157 +622,9 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
684
622
  throw new MicrofrontendError(
685
623
  `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
686
624
  { type: "config", subtype: "multiple_default_applications" }
687
- );
688
- }
689
- };
690
-
691
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
692
- import md5 from "md5";
693
- function hashApplicationName(name) {
694
- if (!name) {
695
- throw new Error("Application name is required to generate hash");
696
- }
697
- return md5(name).substring(0, 6).padStart(6, "0");
698
- }
699
-
700
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
701
- var PREFIX = "vc-ap";
702
- function generateAssetPrefixFromName({
703
- name
704
- }) {
705
- if (!name) {
706
- throw new Error("Name is required to generate an asset prefix");
707
- }
708
- return `${PREFIX}-${hashApplicationName(name)}`;
709
- }
710
-
711
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
712
- function generatePortFromName({
713
- name,
714
- minPort = 3e3,
715
- maxPort = 8e3
716
- }) {
717
- if (!name) {
718
- throw new Error("Name is required to generate a port");
719
- }
720
- let hash = 0;
721
- for (let i = 0; i < name.length; i++) {
722
- hash = (hash << 5) - hash + name.charCodeAt(i);
723
- hash |= 0;
724
- }
725
- hash = Math.abs(hash);
726
- const range = maxPort - minPort;
727
- const port = minPort + hash % range;
728
- return port;
729
- }
730
-
731
- // src/config/microfrontends-config/isomorphic/host.ts
732
- var Host = class {
733
- constructor(hostConfig, options) {
734
- if (typeof hostConfig === "string") {
735
- ({
736
- protocol: this.protocol,
737
- host: this.host,
738
- port: this.port
739
- } = Host.parseUrl(hostConfig));
740
- } else {
741
- const { protocol = "https", host, port } = hostConfig;
742
- this.protocol = protocol;
743
- this.host = host;
744
- this.port = port;
745
- }
746
- this.local = options?.isLocal;
747
- }
748
- static parseUrl(url, defaultProtocol = "https") {
749
- let hostToParse = url;
750
- if (!/^https?:\/\//.exec(hostToParse)) {
751
- hostToParse = `${defaultProtocol}://${hostToParse}`;
752
- }
753
- const parsed = new URL(hostToParse);
754
- if (!parsed.hostname) {
755
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
756
- }
757
- if (parsed.hash) {
758
- throw new Error(
759
- Host.getMicrofrontendsError(url, "cannot have a fragment")
760
- );
761
- }
762
- if (parsed.username || parsed.password) {
763
- throw new Error(
764
- Host.getMicrofrontendsError(
765
- url,
766
- "cannot have authentication credentials (username and/or password)"
767
- )
768
- );
769
- }
770
- if (parsed.pathname !== "/") {
771
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
772
- }
773
- if (parsed.search) {
774
- throw new Error(
775
- Host.getMicrofrontendsError(url, "cannot have query parameters")
776
- );
777
- }
778
- const protocol = parsed.protocol.slice(0, -1);
779
- return {
780
- protocol,
781
- host: parsed.hostname,
782
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
783
- };
784
- }
785
- static getMicrofrontendsError(url, message) {
786
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
787
- }
788
- isLocal() {
789
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
790
- }
791
- toString() {
792
- const url = this.toUrl();
793
- return url.toString().replace(/\/$/, "");
794
- }
795
- toUrl() {
796
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
797
- return new URL(url);
798
- }
799
- };
800
- var LocalHost = class extends Host {
801
- constructor({
802
- appName,
803
- local
804
- }) {
805
- let protocol;
806
- let host;
807
- let port;
808
- if (typeof local === "number") {
809
- port = local;
810
- } else if (typeof local === "string") {
811
- if (/^\d+$/.test(local)) {
812
- port = Number.parseInt(local);
813
- } else {
814
- const parsed = Host.parseUrl(local, "http");
815
- protocol = parsed.protocol;
816
- host = parsed.host;
817
- port = parsed.port;
818
- }
819
- } else if (local) {
820
- protocol = local.protocol;
821
- host = local.host;
822
- port = local.port;
823
- }
824
- super({
825
- protocol: protocol ?? "http",
826
- host: host ?? "localhost",
827
- port: port ?? generatePortFromName({ name: appName })
828
- });
829
- }
830
- };
831
-
832
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
833
- function generateAutomationBypassEnvVarName({
834
- name
835
- }) {
836
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
837
- }
625
+ );
626
+ }
627
+ };
838
628
 
839
629
  // src/config/microfrontends-config/isomorphic/application.ts
840
630
  var Application = class {
@@ -916,9 +706,6 @@ var ChildApplication = class extends Application {
916
706
  }
917
707
  };
918
708
 
919
- // src/config/microfrontends-config/isomorphic/constants.ts
920
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
921
-
922
709
  // src/config/microfrontends-config/isomorphic/index.ts
923
710
  var MicrofrontendConfigIsomorphic = class {
924
711
  constructor({
@@ -962,7 +749,7 @@ var MicrofrontendConfigIsomorphic = class {
962
749
  };
963
750
  }
964
751
  static validate(config) {
965
- const c = typeof config === "string" ? parse2(config) : config;
752
+ const c = typeof config === "string" ? parse(config) : config;
966
753
  validateConfigPaths(c.applications);
967
754
  validateConfigDefaultApplication(c.applications);
968
755
  return c;
@@ -971,7 +758,7 @@ var MicrofrontendConfigIsomorphic = class {
971
758
  cookies
972
759
  }) {
973
760
  return new MicrofrontendConfigIsomorphic({
974
- config: parse2(getConfigStringFromEnv()),
761
+ config: parse(getConfigStringFromEnv()),
975
762
  overrides: parseOverrides(cookies ?? [])
976
763
  });
977
764
  }
@@ -1037,9 +824,17 @@ var MicrofrontendConfigIsomorphic = class {
1037
824
  return this.defaultApplication;
1038
825
  }
1039
826
  /**
1040
- * Returns the configured port for the local proxy
827
+ * Returns the configured port for the local proxy.
828
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
1041
829
  */
1042
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
+ }
1043
838
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1044
839
  }
1045
840
  toClientConfig(options) {
@@ -1077,32 +872,144 @@ var MicrofrontendConfigIsomorphic = class {
1077
872
  }
1078
873
  };
1079
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);
928
+ }
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;
951
+ }
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);
962
+ }
963
+ if (lastPackageJsonDir) {
964
+ return lastPackageJsonDir;
965
+ }
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
+ }
970
+
1080
971
  // src/config/microfrontends/utils/get-application-context.ts
1081
- import fs5 from "node:fs";
1082
- import path4 from "node:path";
972
+ import fs4 from "node:fs";
973
+ import path3 from "node:path";
1083
974
  function getApplicationContext(opts) {
1084
975
  if (opts?.appName) {
976
+ logger.debug(
977
+ "[MFE Config] Application name from appName parameter:",
978
+ opts.appName
979
+ );
1085
980
  return { name: opts.appName };
1086
981
  }
1087
982
  if (process.env.VERCEL_PROJECT_NAME) {
983
+ logger.debug(
984
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
985
+ process.env.VERCEL_PROJECT_NAME
986
+ );
1088
987
  return {
1089
988
  name: process.env.VERCEL_PROJECT_NAME,
1090
989
  projectName: process.env.VERCEL_PROJECT_NAME
1091
990
  };
1092
991
  }
1093
992
  if (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
+ );
1094
997
  return {
1095
998
  name: process.env.NX_TASK_TARGET_PROJECT,
1096
999
  packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1097
1000
  };
1098
1001
  }
1099
1002
  try {
1100
- const vercelProjectJsonPath = fs5.readFileSync(
1101
- path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1003
+ const vercelProjectJsonPath = fs4.readFileSync(
1004
+ path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1102
1005
  "utf-8"
1103
1006
  );
1104
1007
  const projectJson = JSON.parse(vercelProjectJsonPath);
1105
1008
  if (projectJson.projectName) {
1009
+ logger.debug(
1010
+ "[MFE Config] Application name from .vercel/project.json:",
1011
+ projectJson.projectName
1012
+ );
1106
1013
  return {
1107
1014
  name: projectJson.projectName,
1108
1015
  projectName: projectJson.projectName
@@ -1111,8 +1018,8 @@ function getApplicationContext(opts) {
1111
1018
  } catch (_) {
1112
1019
  }
1113
1020
  try {
1114
- const packageJsonString = fs5.readFileSync(
1115
- path4.join(opts?.packageRoot || ".", "package.json"),
1021
+ const packageJsonString = fs4.readFileSync(
1022
+ path3.join(opts?.packageRoot || ".", "package.json"),
1116
1023
  "utf-8"
1117
1024
  );
1118
1025
  const packageJson = JSON.parse(packageJsonString);
@@ -1126,6 +1033,10 @@ function getApplicationContext(opts) {
1126
1033
  }
1127
1034
  );
1128
1035
  }
1036
+ logger.debug(
1037
+ "[MFE Config] Application name from package.json:",
1038
+ packageJson.name
1039
+ );
1129
1040
  return { name: packageJson.name, packageJsonName: packageJson.name };
1130
1041
  } catch (err) {
1131
1042
  throw MicrofrontendError.handle(err, {
@@ -1134,6 +1045,209 @@ function getApplicationContext(opts) {
1134
1045
  }
1135
1046
  }
1136
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
+
1137
1251
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1138
1252
  import path5 from "node:path";
1139
1253
 
@@ -1147,8 +1261,8 @@ function getOutputFilePath() {
1147
1261
  }
1148
1262
 
1149
1263
  // src/config/microfrontends/server/validation.ts
1150
- import { parse as parse3 } from "jsonc-parser";
1151
1264
  import { Ajv } from "ajv";
1265
+ import { parse as parse3 } from "jsonc-parser";
1152
1266
 
1153
1267
  // schema/schema.json
1154
1268
  var schema_default = {
@@ -1176,9 +1290,7 @@ var schema_default = {
1176
1290
  description: "Optional configuration options for the microfrontend."
1177
1291
  }
1178
1292
  },
1179
- required: [
1180
- "applications"
1181
- ],
1293
+ required: ["applications"],
1182
1294
  additionalProperties: false,
1183
1295
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1184
1296
  },
@@ -1215,19 +1327,14 @@ var schema_default = {
1215
1327
  description: "Development configuration for the default application."
1216
1328
  }
1217
1329
  },
1218
- required: [
1219
- "development"
1220
- ],
1330
+ required: ["development"],
1221
1331
  additionalProperties: false
1222
1332
  },
1223
1333
  DefaultDevelopment: {
1224
1334
  type: "object",
1225
1335
  properties: {
1226
1336
  local: {
1227
- type: [
1228
- "number",
1229
- "string"
1230
- ],
1337
+ type: ["number", "string"],
1231
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."
1232
1339
  },
1233
1340
  task: {
@@ -1239,9 +1346,7 @@ var schema_default = {
1239
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."
1240
1347
  }
1241
1348
  },
1242
- required: [
1243
- "fallback"
1244
- ],
1349
+ required: ["fallback"],
1245
1350
  additionalProperties: false
1246
1351
  },
1247
1352
  ChildApplication: {
@@ -1264,19 +1369,14 @@ var schema_default = {
1264
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."
1265
1370
  }
1266
1371
  },
1267
- required: [
1268
- "routing"
1269
- ],
1372
+ required: ["routing"],
1270
1373
  additionalProperties: false
1271
1374
  },
1272
1375
  ChildDevelopment: {
1273
1376
  type: "object",
1274
1377
  properties: {
1275
1378
  local: {
1276
- type: [
1277
- "number",
1278
- "string"
1279
- ],
1379
+ type: ["number", "string"],
1280
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."
1281
1381
  },
1282
1382
  task: {
@@ -1316,9 +1416,7 @@ var schema_default = {
1316
1416
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1317
1417
  }
1318
1418
  },
1319
- required: [
1320
- "paths"
1321
- ],
1419
+ required: ["paths"],
1322
1420
  additionalProperties: false,
1323
1421
  description: "A group of paths that is routed to this application."
1324
1422
  },
@@ -1497,7 +1595,13 @@ var MicrofrontendsServer = class {
1497
1595
  filePath,
1498
1596
  cookies
1499
1597
  } = {}) {
1598
+ logger.debug("[MFE Config] Starting config inference", {
1599
+ appName,
1600
+ directory: directory || process.cwd(),
1601
+ filePath
1602
+ });
1500
1603
  if (filePath) {
1604
+ logger.debug("[MFE Config] Using explicit filePath:", filePath);
1501
1605
  return MicrofrontendsServer.fromFile({
1502
1606
  filePath,
1503
1607
  cookies
@@ -1505,16 +1609,25 @@ var MicrofrontendsServer = class {
1505
1609
  }
1506
1610
  try {
1507
1611
  const packageRoot = findPackageRoot(directory);
1612
+ logger.debug("[MFE Config] Package root:", packageRoot);
1508
1613
  const applicationContext = getApplicationContext({
1509
1614
  appName,
1510
1615
  packageRoot
1511
1616
  });
1617
+ logger.debug("[MFE Config] Application context:", applicationContext);
1512
1618
  const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
1619
+ if (customConfigFilename) {
1620
+ logger.debug(
1621
+ "[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
1622
+ customConfigFilename
1623
+ );
1624
+ }
1513
1625
  const maybeConfig = findConfig({
1514
1626
  dir: packageRoot,
1515
1627
  customConfigFilename
1516
1628
  });
1517
1629
  if (maybeConfig) {
1630
+ logger.debug("[MFE Config] Config found at package root:", maybeConfig);
1518
1631
  return MicrofrontendsServer.fromFile({
1519
1632
  filePath: maybeConfig,
1520
1633
  cookies
@@ -1522,42 +1635,78 @@ var MicrofrontendsServer = class {
1522
1635
  }
1523
1636
  const repositoryRoot = findRepositoryRoot();
1524
1637
  const isMonorepo2 = isMonorepo({ repositoryRoot });
1638
+ logger.debug(
1639
+ "[MFE Config] Repository root:",
1640
+ repositoryRoot,
1641
+ "Is monorepo:",
1642
+ isMonorepo2
1643
+ );
1525
1644
  const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
1526
1645
  if (typeof configFromEnv === "string") {
1646
+ logger.debug(
1647
+ "[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
1648
+ configFromEnv
1649
+ );
1527
1650
  const maybeConfigFromEnv = resolve(packageRoot, configFromEnv);
1528
1651
  if (maybeConfigFromEnv) {
1652
+ logger.debug(
1653
+ "[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
1654
+ maybeConfigFromEnv
1655
+ );
1529
1656
  return MicrofrontendsServer.fromFile({
1530
1657
  filePath: maybeConfigFromEnv,
1531
1658
  cookies
1532
1659
  });
1533
1660
  }
1534
1661
  } else {
1662
+ const vercelDir = join3(packageRoot, ".vercel");
1663
+ logger.debug(
1664
+ "[MFE Config] Searching for config in .vercel directory:",
1665
+ vercelDir
1666
+ );
1535
1667
  const maybeConfigFromVercel = findConfig({
1536
- dir: join2(packageRoot, ".vercel"),
1668
+ dir: vercelDir,
1537
1669
  customConfigFilename
1538
1670
  });
1539
1671
  if (maybeConfigFromVercel) {
1672
+ logger.debug(
1673
+ "[MFE Config] Config found in .vercel directory:",
1674
+ maybeConfigFromVercel
1675
+ );
1540
1676
  return MicrofrontendsServer.fromFile({
1541
1677
  filePath: maybeConfigFromVercel,
1542
1678
  cookies
1543
1679
  });
1544
1680
  }
1545
1681
  if (isMonorepo2) {
1682
+ logger.debug(
1683
+ "[MFE Config] Inferring microfrontends location in monorepo for application:",
1684
+ applicationContext.name
1685
+ );
1546
1686
  const defaultPackage = inferMicrofrontendsLocation({
1547
1687
  repositoryRoot,
1548
1688
  applicationContext,
1549
1689
  customConfigFilename
1550
1690
  });
1691
+ logger.debug(
1692
+ "[MFE Config] Inferred package location:",
1693
+ defaultPackage
1694
+ );
1551
1695
  const maybeConfigFromDefault = findConfig({
1552
1696
  dir: defaultPackage,
1553
1697
  customConfigFilename
1554
1698
  });
1555
1699
  if (maybeConfigFromDefault) {
1700
+ logger.debug(
1701
+ "[MFE Config] Config found in inferred package:",
1702
+ maybeConfigFromDefault
1703
+ );
1556
1704
  return MicrofrontendsServer.fromFile({
1557
1705
  filePath: maybeConfigFromDefault,
1558
1706
  cookies
1559
1707
  });
1560
1708
  }
1709
+ logger.debug("[MFE Config] No config found in inferred package");
1561
1710
  }
1562
1711
  }
1563
1712
  throw new MicrofrontendError(
@@ -1583,8 +1732,13 @@ var MicrofrontendsServer = class {
1583
1732
  cookies
1584
1733
  }) {
1585
1734
  try {
1735
+ logger.debug("[MFE Config] Reading config from file:", filePath);
1586
1736
  const configJson = fs6.readFileSync(filePath, "utf-8");
1587
1737
  const config = MicrofrontendsServer.validate(configJson);
1738
+ logger.debug(
1739
+ "[MFE Config] Config loaded with applications:",
1740
+ Object.keys(config.applications)
1741
+ );
1588
1742
  return new MicrofrontendsServer({
1589
1743
  config,
1590
1744
  overrides: cookies ? parseOverrides(cookies) : void 0