@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
@@ -43,40 +43,7 @@ function displayLocalProxyInfo(port) {
43
43
 
44
44
  // src/config/microfrontends/server/index.ts
45
45
  import fs6 from "node:fs";
46
- import { dirname as dirname2, join as join2, resolve } from "node:path";
47
-
48
- // src/config/overrides/constants.ts
49
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
50
- var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
51
-
52
- // src/config/overrides/is-override-cookie.ts
53
- function isOverrideCookie(cookie) {
54
- return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
55
- }
56
-
57
- // src/config/overrides/get-override-from-cookie.ts
58
- function getOverrideFromCookie(cookie) {
59
- if (!isOverrideCookie(cookie) || !cookie.value)
60
- return;
61
- return {
62
- application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
63
- host: cookie.value
64
- };
65
- }
66
-
67
- // src/config/overrides/parse-overrides.ts
68
- function parseOverrides(cookies) {
69
- const overridesConfig = { applications: {} };
70
- cookies.forEach((cookie) => {
71
- const override = getOverrideFromCookie(cookie);
72
- if (!override)
73
- return;
74
- overridesConfig.applications[override.application] = {
75
- environment: { host: override.host }
76
- };
77
- });
78
- return overridesConfig;
79
- }
46
+ import { dirname as dirname2, join as join3, resolve } from "node:path";
80
47
 
81
48
  // src/config/errors.ts
82
49
  var MicrofrontendError = class extends Error {
@@ -170,253 +137,47 @@ var MicrofrontendError = class extends Error {
170
137
  }
171
138
  };
172
139
 
173
- // src/config/microfrontends-config/utils/get-config-from-env.ts
174
- function getConfigStringFromEnv() {
175
- const config = process.env.MFE_CONFIG;
176
- if (!config) {
177
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
178
- type: "config",
179
- subtype: "not_found_in_env"
180
- });
181
- }
182
- return config;
183
- }
184
-
185
- // src/config/schema/utils/is-default-app.ts
186
- function isDefaultApp(a) {
187
- return !("routing" in a);
188
- }
189
-
190
- // src/config/microfrontends/utils/find-repository-root.ts
191
- import fs from "node:fs";
192
- import path from "node:path";
193
- var GIT_DIRECTORY = ".git";
194
- function hasGitDirectory(dir) {
195
- const gitPath = path.join(dir, GIT_DIRECTORY);
196
- return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
197
- }
198
- function hasPnpmWorkspaces(dir) {
199
- return fs.existsSync(path.join(dir, "pnpm-workspace.yaml"));
200
- }
201
- function hasPackageJson(dir) {
202
- return fs.existsSync(path.join(dir, "package.json"));
203
- }
204
- function findRepositoryRoot(startDir) {
205
- if (process.env.NX_WORKSPACE_ROOT) {
206
- return process.env.NX_WORKSPACE_ROOT;
207
- }
208
- let currentDir = startDir || process.cwd();
209
- let lastPackageJsonDir = null;
210
- while (currentDir !== path.parse(currentDir).root) {
211
- if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
212
- return currentDir;
213
- }
214
- if (hasPackageJson(currentDir)) {
215
- lastPackageJsonDir = currentDir;
216
- }
217
- currentDir = path.dirname(currentDir);
218
- }
219
- if (lastPackageJsonDir) {
220
- return lastPackageJsonDir;
221
- }
222
- throw new Error(
223
- `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.`
224
- );
225
- }
226
-
227
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
228
- import { dirname } from "node:path";
229
- import { readFileSync } from "node:fs";
140
+ // src/config/microfrontends-config/isomorphic/index.ts
230
141
  import { parse } from "jsonc-parser";
231
- import fg from "fast-glob";
232
142
 
233
- // src/config/microfrontends/utils/get-config-file-name.ts
234
- var DEFAULT_CONFIGURATION_FILENAMES = [
235
- "microfrontends.json",
236
- "microfrontends.jsonc"
237
- ];
238
- function getPossibleConfigurationFilenames({
239
- customConfigFilename
240
- }) {
241
- if (customConfigFilename) {
242
- if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
243
- throw new Error(
244
- `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.`
245
- );
246
- }
247
- return Array.from(
248
- /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
249
- );
250
- }
251
- return DEFAULT_CONFIGURATION_FILENAMES;
252
- }
253
-
254
- // src/config/microfrontends/utils/infer-microfrontends-location.ts
255
- var configCache = {};
256
- function findPackageWithMicrofrontendsConfig({
257
- repositoryRoot,
258
- applicationContext,
259
- customConfigFilename
260
- }) {
261
- const applicationName = applicationContext.name;
262
- try {
263
- const microfrontendsJsonPaths = fg.globSync(
264
- `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
265
- {
266
- cwd: repositoryRoot,
267
- absolute: true,
268
- onlyFiles: true,
269
- followSymbolicLinks: false,
270
- ignore: ["**/node_modules/**", "**/.git/**"]
271
- }
272
- );
273
- const matchingPaths = [];
274
- for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
275
- try {
276
- const microfrontendsJsonContent = readFileSync(
277
- microfrontendsJsonPath,
278
- "utf-8"
279
- );
280
- const microfrontendsJson = parse(microfrontendsJsonContent);
281
- if (microfrontendsJson.applications[applicationName]) {
282
- matchingPaths.push(microfrontendsJsonPath);
283
- } else {
284
- for (const [_, app] of Object.entries(
285
- microfrontendsJson.applications
286
- )) {
287
- if (app.packageName === applicationName) {
288
- matchingPaths.push(microfrontendsJsonPath);
289
- }
290
- }
291
- }
292
- } catch (error2) {
293
- }
294
- }
295
- if (matchingPaths.length > 1) {
296
- throw new MicrofrontendError(
297
- `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
298
- ${matchingPaths.join("\n \u2022 ")}`,
299
- { type: "config", subtype: "inference_failed" }
300
- );
301
- }
302
- if (matchingPaths.length === 0) {
303
- let additionalErrorMessage = "";
304
- if (microfrontendsJsonPaths.length > 0) {
305
- if (!applicationContext.projectName) {
306
- additionalErrorMessage = `
307
-
308
- If the name in package.json (${applicationContext.packageJsonName}) differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`;
309
- } else {
310
- additionalErrorMessage = `
311
-
312
- Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
313
- }
314
- }
315
- throw new MicrofrontendError(
316
- `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
317
-
318
- If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable.
319
-
320
- If your Vercel Microfrontends configuration has a custom name, ensure the VC_MICROFRONTENDS_CONFIG_FILE_NAME environment variable is set, you can pull the vercel project environment variables using the "vercel env pull" command.
143
+ // src/config/overrides/constants.ts
144
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
145
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
321
146
 
322
- If you suspect this is thrown in error, please reach out to the Vercel team.`,
323
- { type: "config", subtype: "inference_failed" }
324
- );
325
- }
326
- const [packageJsonPath] = matchingPaths;
327
- return dirname(packageJsonPath);
328
- } catch (error2) {
329
- if (error2 instanceof MicrofrontendError) {
330
- throw error2;
331
- }
332
- return null;
333
- }
334
- }
335
- function inferMicrofrontendsLocation(opts) {
336
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
337
- if (configCache[cacheKey]) {
338
- return configCache[cacheKey];
339
- }
340
- const result = findPackageWithMicrofrontendsConfig(opts);
341
- if (!result) {
342
- throw new MicrofrontendError(
343
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
344
- { type: "config", subtype: "inference_failed" }
345
- );
346
- }
347
- configCache[cacheKey] = result;
348
- return result;
147
+ // src/config/overrides/is-override-cookie.ts
148
+ function isOverrideCookie(cookie) {
149
+ return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
349
150
  }
350
151
 
351
- // src/config/microfrontends/utils/is-monorepo.ts
352
- import fs2 from "node:fs";
353
- import path2 from "node:path";
354
- function isMonorepo({
355
- repositoryRoot
356
- }) {
357
- try {
358
- if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
359
- return true;
360
- }
361
- if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
362
- return true;
363
- }
364
- if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
365
- return true;
366
- }
367
- const packageJsonPath = path2.join(repositoryRoot, "package.json");
368
- if (!fs2.existsSync(packageJsonPath)) {
369
- return false;
370
- }
371
- const packageJson = JSON.parse(
372
- fs2.readFileSync(packageJsonPath, "utf-8")
373
- );
374
- return packageJson.workspaces !== void 0;
375
- } catch (error2) {
376
- logger.error("Error determining if repository is a monorepo", error2);
377
- return false;
378
- }
152
+ // src/config/overrides/get-override-from-cookie.ts
153
+ function getOverrideFromCookie(cookie) {
154
+ if (!isOverrideCookie(cookie) || !cookie.value)
155
+ return;
156
+ return {
157
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
158
+ host: cookie.value
159
+ };
379
160
  }
380
161
 
381
- // src/config/microfrontends/utils/find-package-root.ts
382
- import fs3 from "node:fs";
383
- import path3 from "node:path";
384
- var PACKAGE_JSON = "package.json";
385
- function findPackageRoot(startDir) {
386
- let currentDir = startDir || process.cwd();
387
- while (currentDir !== path3.parse(currentDir).root) {
388
- const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
389
- if (fs3.existsSync(pkgJsonPath)) {
390
- return currentDir;
391
- }
392
- currentDir = path3.dirname(currentDir);
393
- }
394
- throw new Error(
395
- `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
396
- );
162
+ // src/config/overrides/parse-overrides.ts
163
+ function parseOverrides(cookies) {
164
+ const overridesConfig = { applications: {} };
165
+ cookies.forEach((cookie) => {
166
+ const override = getOverrideFromCookie(cookie);
167
+ if (!override)
168
+ return;
169
+ overridesConfig.applications[override.application] = {
170
+ environment: { host: override.host }
171
+ };
172
+ });
173
+ return overridesConfig;
397
174
  }
398
175
 
399
- // src/config/microfrontends/utils/find-config.ts
400
- import fs4 from "node:fs";
401
- import { join } from "node:path";
402
- function findConfig({
403
- dir,
404
- customConfigFilename
405
- }) {
406
- for (const filename of getPossibleConfigurationFilenames({
407
- customConfigFilename
408
- })) {
409
- const maybeConfig = join(dir, filename);
410
- if (fs4.existsSync(maybeConfig)) {
411
- return maybeConfig;
412
- }
413
- }
414
- return null;
176
+ // src/config/schema/utils/is-default-app.ts
177
+ function isDefaultApp(a) {
178
+ return !("routing" in a);
415
179
  }
416
180
 
417
- // src/config/microfrontends-config/isomorphic/index.ts
418
- import { parse as parse2 } from "jsonc-parser";
419
-
420
181
  // src/config/microfrontends-config/client/index.ts
421
182
  import { pathToRegexp } from "path-to-regexp";
422
183
  var regexpCache = /* @__PURE__ */ new Map();
@@ -477,46 +238,223 @@ var MicrofrontendConfigClient = class {
477
238
  }
478
239
  return new MicrofrontendConfigClient(JSON.parse(config));
479
240
  }
480
- isEqual(other) {
481
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
241
+ isEqual(other) {
242
+ return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
243
+ }
244
+ getApplicationNameForPath(path6) {
245
+ if (!path6.startsWith("/")) {
246
+ throw new Error(`Path must start with a /`);
247
+ }
248
+ if (this.pathCache[path6]) {
249
+ return this.pathCache[path6];
250
+ }
251
+ const pathname = new URL(path6, "https://example.com").pathname;
252
+ for (const [name, application] of Object.entries(this.applications)) {
253
+ if (application.routing) {
254
+ for (const group of application.routing) {
255
+ for (const childPath of group.paths) {
256
+ const regexp = getRegexp(childPath);
257
+ if (regexp.test(pathname)) {
258
+ this.pathCache[path6] = name;
259
+ return name;
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ const defaultApplication = Object.entries(this.applications).find(
266
+ ([, application]) => application.default
267
+ );
268
+ if (!defaultApplication) {
269
+ return null;
270
+ }
271
+ this.pathCache[path6] = defaultApplication[0];
272
+ return defaultApplication[0];
273
+ }
274
+ serialize() {
275
+ return this.serialized;
276
+ }
277
+ };
278
+
279
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
280
+ function getConfigStringFromEnv() {
281
+ const config = process.env.MFE_CONFIG;
282
+ if (!config) {
283
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
284
+ type: "config",
285
+ subtype: "not_found_in_env"
286
+ });
287
+ }
288
+ return config;
289
+ }
290
+
291
+ // src/config/microfrontends-config/isomorphic/constants.ts
292
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
293
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
294
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
295
+
296
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
297
+ function generatePortFromName({
298
+ name,
299
+ minPort = 3e3,
300
+ maxPort = 8e3
301
+ }) {
302
+ if (!name) {
303
+ throw new Error("Name is required to generate a port");
304
+ }
305
+ let hash = 0;
306
+ for (let i = 0; i < name.length; i++) {
307
+ hash = (hash << 5) - hash + name.charCodeAt(i);
308
+ hash |= 0;
309
+ }
310
+ hash = Math.abs(hash);
311
+ const range = maxPort - minPort;
312
+ const port = minPort + hash % range;
313
+ return port;
314
+ }
315
+
316
+ // src/config/microfrontends-config/isomorphic/host.ts
317
+ var Host = class {
318
+ constructor(hostConfig, options) {
319
+ if (typeof hostConfig === "string") {
320
+ ({
321
+ protocol: this.protocol,
322
+ host: this.host,
323
+ port: this.port
324
+ } = Host.parseUrl(hostConfig));
325
+ } else {
326
+ const { protocol = "https", host, port } = hostConfig;
327
+ this.protocol = protocol;
328
+ this.host = host;
329
+ this.port = port;
330
+ }
331
+ this.local = options?.isLocal;
332
+ }
333
+ static parseUrl(url, defaultProtocol = "https") {
334
+ let hostToParse = url;
335
+ if (!/^https?:\/\//.exec(hostToParse)) {
336
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
337
+ }
338
+ const parsed = new URL(hostToParse);
339
+ if (!parsed.hostname) {
340
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
341
+ }
342
+ if (parsed.hash) {
343
+ throw new Error(
344
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
345
+ );
346
+ }
347
+ if (parsed.username || parsed.password) {
348
+ throw new Error(
349
+ Host.getMicrofrontendsError(
350
+ url,
351
+ "cannot have authentication credentials (username and/or password)"
352
+ )
353
+ );
354
+ }
355
+ if (parsed.pathname !== "/") {
356
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
357
+ }
358
+ if (parsed.search) {
359
+ throw new Error(
360
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
361
+ );
362
+ }
363
+ const protocol = parsed.protocol.slice(0, -1);
364
+ return {
365
+ protocol,
366
+ host: parsed.hostname,
367
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
368
+ };
369
+ }
370
+ static getMicrofrontendsError(url, message) {
371
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
372
+ }
373
+ isLocal() {
374
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
375
+ }
376
+ toString() {
377
+ const url = this.toUrl();
378
+ return url.toString().replace(/\/$/, "");
379
+ }
380
+ toUrl() {
381
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
382
+ return new URL(url);
482
383
  }
483
- getApplicationNameForPath(path6) {
484
- if (!path6.startsWith("/")) {
485
- throw new Error(`Path must start with a /`);
486
- }
487
- if (this.pathCache[path6]) {
488
- return this.pathCache[path6];
489
- }
490
- const pathname = new URL(path6, "https://example.com").pathname;
491
- for (const [name, application] of Object.entries(this.applications)) {
492
- if (application.routing) {
493
- for (const group of application.routing) {
494
- for (const childPath of group.paths) {
495
- const regexp = getRegexp(childPath);
496
- if (regexp.test(pathname)) {
497
- this.pathCache[path6] = name;
498
- return name;
499
- }
500
- }
501
- }
384
+ };
385
+ var LocalHost = class extends Host {
386
+ constructor({
387
+ appName,
388
+ local
389
+ }) {
390
+ const portOverride = process.env[MFE_APP_PORT_ENV];
391
+ if (portOverride) {
392
+ const overridePort = Number.parseInt(portOverride, 10);
393
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
394
+ super({
395
+ protocol: "http",
396
+ host: "localhost",
397
+ port: overridePort
398
+ });
399
+ return;
502
400
  }
503
401
  }
504
- const defaultApplication = Object.entries(this.applications).find(
505
- ([, application]) => application.default
506
- );
507
- if (!defaultApplication) {
508
- return null;
402
+ let protocol;
403
+ let host;
404
+ let port;
405
+ if (typeof local === "number") {
406
+ port = local;
407
+ } else if (typeof local === "string") {
408
+ if (/^\d+$/.test(local)) {
409
+ port = Number.parseInt(local, 10);
410
+ } else {
411
+ const parsed = Host.parseUrl(local, "http");
412
+ protocol = parsed.protocol;
413
+ host = parsed.host;
414
+ port = parsed.port;
415
+ }
416
+ } else if (local) {
417
+ protocol = local.protocol;
418
+ host = local.host;
419
+ port = local.port;
509
420
  }
510
- this.pathCache[path6] = defaultApplication[0];
511
- return defaultApplication[0];
512
- }
513
- serialize() {
514
- return this.serialized;
421
+ super({
422
+ protocol: protocol ?? "http",
423
+ host: host ?? "localhost",
424
+ port: port ?? generatePortFromName({ name: appName })
425
+ });
515
426
  }
516
427
  };
517
428
 
429
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
430
+ import md5 from "md5";
431
+ function hashApplicationName(name) {
432
+ if (!name) {
433
+ throw new Error("Application name is required to generate hash");
434
+ }
435
+ return md5(name).substring(0, 6).padStart(6, "0");
436
+ }
437
+
438
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
439
+ var PREFIX = "vc-ap";
440
+ function generateAssetPrefixFromName({
441
+ name
442
+ }) {
443
+ if (!name) {
444
+ throw new Error("Name is required to generate an asset prefix");
445
+ }
446
+ return `${PREFIX}-${hashApplicationName(name)}`;
447
+ }
448
+
449
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
450
+ function generateAutomationBypassEnvVarName({
451
+ name
452
+ }) {
453
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
454
+ }
455
+
518
456
  // src/config/microfrontends-config/isomorphic/validation.ts
519
- import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
457
+ import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
520
458
  var LIST_FORMATTER = new Intl.ListFormat("en", {
521
459
  style: "long",
522
460
  type: "conjunction"
@@ -670,180 +608,32 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
670
608
  if (!applicationConfigsById) {
671
609
  return;
672
610
  }
673
- const applicationsWithoutRouting = Object.entries(
674
- applicationConfigsById
675
- ).filter(([, app]) => isDefaultApp(app));
676
- const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
677
- (acc) => {
678
- return acc + 1;
679
- },
680
- 0
681
- );
682
- if (numApplicationsWithoutRouting === 0) {
683
- throw new MicrofrontendError(
684
- "No default application found. At least one application needs to be the default by omitting routing.",
685
- { type: "config", subtype: "no_default_application" }
686
- );
687
- }
688
- if (numApplicationsWithoutRouting > 1) {
689
- const applicationNamesMissingRouting = applicationsWithoutRouting.map(
690
- ([name]) => name
691
- );
692
- throw new MicrofrontendError(
693
- `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
694
- { type: "config", subtype: "multiple_default_applications" }
695
- );
696
- }
697
- };
698
-
699
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
700
- import md5 from "md5";
701
- function hashApplicationName(name) {
702
- if (!name) {
703
- throw new Error("Application name is required to generate hash");
704
- }
705
- return md5(name).substring(0, 6).padStart(6, "0");
706
- }
707
-
708
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
709
- var PREFIX = "vc-ap";
710
- function generateAssetPrefixFromName({
711
- name
712
- }) {
713
- if (!name) {
714
- throw new Error("Name is required to generate an asset prefix");
715
- }
716
- return `${PREFIX}-${hashApplicationName(name)}`;
717
- }
718
-
719
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
720
- function generatePortFromName({
721
- name,
722
- minPort = 3e3,
723
- maxPort = 8e3
724
- }) {
725
- if (!name) {
726
- throw new Error("Name is required to generate a port");
727
- }
728
- let hash = 0;
729
- for (let i = 0; i < name.length; i++) {
730
- hash = (hash << 5) - hash + name.charCodeAt(i);
731
- hash |= 0;
732
- }
733
- hash = Math.abs(hash);
734
- const range = maxPort - minPort;
735
- const port = minPort + hash % range;
736
- return port;
737
- }
738
-
739
- // src/config/microfrontends-config/isomorphic/host.ts
740
- var Host = class {
741
- constructor(hostConfig, options) {
742
- if (typeof hostConfig === "string") {
743
- ({
744
- protocol: this.protocol,
745
- host: this.host,
746
- port: this.port
747
- } = Host.parseUrl(hostConfig));
748
- } else {
749
- const { protocol = "https", host, port } = hostConfig;
750
- this.protocol = protocol;
751
- this.host = host;
752
- this.port = port;
753
- }
754
- this.local = options?.isLocal;
755
- }
756
- static parseUrl(url, defaultProtocol = "https") {
757
- let hostToParse = url;
758
- if (!/^https?:\/\//.exec(hostToParse)) {
759
- hostToParse = `${defaultProtocol}://${hostToParse}`;
760
- }
761
- const parsed = new URL(hostToParse);
762
- if (!parsed.hostname) {
763
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
764
- }
765
- if (parsed.hash) {
766
- throw new Error(
767
- Host.getMicrofrontendsError(url, "cannot have a fragment")
768
- );
769
- }
770
- if (parsed.username || parsed.password) {
771
- throw new Error(
772
- Host.getMicrofrontendsError(
773
- url,
774
- "cannot have authentication credentials (username and/or password)"
775
- )
776
- );
777
- }
778
- if (parsed.pathname !== "/") {
779
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
780
- }
781
- if (parsed.search) {
782
- throw new Error(
783
- Host.getMicrofrontendsError(url, "cannot have query parameters")
784
- );
785
- }
786
- const protocol = parsed.protocol.slice(0, -1);
787
- return {
788
- protocol,
789
- host: parsed.hostname,
790
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
791
- };
792
- }
793
- static getMicrofrontendsError(url, message) {
794
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
795
- }
796
- isLocal() {
797
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
798
- }
799
- toString() {
800
- const url = this.toUrl();
801
- return url.toString().replace(/\/$/, "");
802
- }
803
- toUrl() {
804
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
805
- return new URL(url);
806
- }
807
- };
808
- var LocalHost = class extends Host {
809
- constructor({
810
- appName,
811
- local
812
- }) {
813
- let protocol;
814
- let host;
815
- let port;
816
- if (typeof local === "number") {
817
- port = local;
818
- } else if (typeof local === "string") {
819
- if (/^\d+$/.test(local)) {
820
- port = Number.parseInt(local);
821
- } else {
822
- const parsed = Host.parseUrl(local, "http");
823
- protocol = parsed.protocol;
824
- host = parsed.host;
825
- port = parsed.port;
826
- }
827
- } else if (local) {
828
- protocol = local.protocol;
829
- host = local.host;
830
- port = local.port;
831
- }
832
- super({
833
- protocol: protocol ?? "http",
834
- host: host ?? "localhost",
835
- port: port ?? generatePortFromName({ name: appName })
836
- });
837
- }
611
+ const applicationsWithoutRouting = Object.entries(
612
+ applicationConfigsById
613
+ ).filter(([, app]) => isDefaultApp(app));
614
+ const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
615
+ (acc) => {
616
+ return acc + 1;
617
+ },
618
+ 0
619
+ );
620
+ if (numApplicationsWithoutRouting === 0) {
621
+ throw new MicrofrontendError(
622
+ "No default application found. At least one application needs to be the default by omitting routing.",
623
+ { type: "config", subtype: "no_default_application" }
624
+ );
625
+ }
626
+ if (numApplicationsWithoutRouting > 1) {
627
+ const applicationNamesMissingRouting = applicationsWithoutRouting.map(
628
+ ([name]) => name
629
+ );
630
+ throw new MicrofrontendError(
631
+ `All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
632
+ { type: "config", subtype: "multiple_default_applications" }
633
+ );
634
+ }
838
635
  };
839
636
 
840
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
841
- function generateAutomationBypassEnvVarName({
842
- name
843
- }) {
844
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
845
- }
846
-
847
637
  // src/config/microfrontends-config/isomorphic/application.ts
848
638
  var Application = class {
849
639
  constructor(name, {
@@ -924,9 +714,6 @@ var ChildApplication = class extends Application {
924
714
  }
925
715
  };
926
716
 
927
- // src/config/microfrontends-config/isomorphic/constants.ts
928
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
929
-
930
717
  // src/config/microfrontends-config/isomorphic/index.ts
931
718
  var MicrofrontendConfigIsomorphic = class {
932
719
  constructor({
@@ -970,7 +757,7 @@ var MicrofrontendConfigIsomorphic = class {
970
757
  };
971
758
  }
972
759
  static validate(config) {
973
- const c = typeof config === "string" ? parse2(config) : config;
760
+ const c = typeof config === "string" ? parse(config) : config;
974
761
  validateConfigPaths(c.applications);
975
762
  validateConfigDefaultApplication(c.applications);
976
763
  return c;
@@ -979,7 +766,7 @@ var MicrofrontendConfigIsomorphic = class {
979
766
  cookies
980
767
  }) {
981
768
  return new MicrofrontendConfigIsomorphic({
982
- config: parse2(getConfigStringFromEnv()),
769
+ config: parse(getConfigStringFromEnv()),
983
770
  overrides: parseOverrides(cookies ?? [])
984
771
  });
985
772
  }
@@ -1045,9 +832,17 @@ var MicrofrontendConfigIsomorphic = class {
1045
832
  return this.defaultApplication;
1046
833
  }
1047
834
  /**
1048
- * Returns the configured port for the local proxy
835
+ * Returns the configured port for the local proxy.
836
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
1049
837
  */
1050
838
  getLocalProxyPort() {
839
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
840
+ if (portOverride) {
841
+ const port = Number.parseInt(portOverride, 10);
842
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
843
+ return port;
844
+ }
845
+ }
1051
846
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
1052
847
  }
1053
848
  toClientConfig(options) {
@@ -1070,75 +865,394 @@ var MicrofrontendConfigIsomorphic = class {
1070
865
  {
1071
866
  removeFlaggedPaths: options?.removeFlaggedPaths
1072
867
  }
1073
- );
1074
- }
1075
- /**
1076
- * Serializes the class back to the Schema type.
1077
- *
1078
- * NOTE: This is used when writing the config to disk and must always match the input Schema
1079
- */
1080
- toSchemaJson() {
1081
- return this.serialized.config;
1082
- }
1083
- serialize() {
1084
- return this.serialized;
1085
- }
1086
- };
868
+ );
869
+ }
870
+ /**
871
+ * Serializes the class back to the Schema type.
872
+ *
873
+ * NOTE: This is used when writing the config to disk and must always match the input Schema
874
+ */
875
+ toSchemaJson() {
876
+ return this.serialized.config;
877
+ }
878
+ serialize() {
879
+ return this.serialized;
880
+ }
881
+ };
882
+
883
+ // src/config/microfrontends/utils/find-config.ts
884
+ import fs from "node:fs";
885
+ import { join } from "node:path";
886
+
887
+ // src/config/microfrontends/utils/get-config-file-name.ts
888
+ var DEFAULT_CONFIGURATION_FILENAMES = [
889
+ "microfrontends.json",
890
+ "microfrontends.jsonc"
891
+ ];
892
+ function getPossibleConfigurationFilenames({
893
+ customConfigFilename
894
+ }) {
895
+ if (customConfigFilename) {
896
+ if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
897
+ throw new Error(
898
+ `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.`
899
+ );
900
+ }
901
+ return Array.from(
902
+ /* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
903
+ );
904
+ }
905
+ return DEFAULT_CONFIGURATION_FILENAMES;
906
+ }
907
+
908
+ // src/config/microfrontends/utils/find-config.ts
909
+ function findConfig({
910
+ dir,
911
+ customConfigFilename
912
+ }) {
913
+ for (const filename of getPossibleConfigurationFilenames({
914
+ customConfigFilename
915
+ })) {
916
+ const maybeConfig = join(dir, filename);
917
+ if (fs.existsSync(maybeConfig)) {
918
+ return maybeConfig;
919
+ }
920
+ }
921
+ return null;
922
+ }
923
+
924
+ // src/config/microfrontends/utils/find-package-root.ts
925
+ import fs2 from "node:fs";
926
+ import path from "node:path";
927
+ var PACKAGE_JSON = "package.json";
928
+ function findPackageRoot(startDir) {
929
+ let currentDir = startDir || process.cwd();
930
+ while (currentDir !== path.parse(currentDir).root) {
931
+ const pkgJsonPath = path.join(currentDir, PACKAGE_JSON);
932
+ if (fs2.existsSync(pkgJsonPath)) {
933
+ return currentDir;
934
+ }
935
+ currentDir = path.dirname(currentDir);
936
+ }
937
+ throw new Error(
938
+ `The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
939
+ );
940
+ }
941
+
942
+ // src/config/microfrontends/utils/find-repository-root.ts
943
+ import fs3 from "node:fs";
944
+ import path2 from "node:path";
945
+ var GIT_DIRECTORY = ".git";
946
+ function hasGitDirectory(dir) {
947
+ const gitPath = path2.join(dir, GIT_DIRECTORY);
948
+ return fs3.existsSync(gitPath) && fs3.statSync(gitPath).isDirectory();
949
+ }
950
+ function hasPnpmWorkspaces(dir) {
951
+ return fs3.existsSync(path2.join(dir, "pnpm-workspace.yaml"));
952
+ }
953
+ function hasPackageJson(dir) {
954
+ return fs3.existsSync(path2.join(dir, "package.json"));
955
+ }
956
+ function findRepositoryRoot(startDir) {
957
+ if (process.env.NX_WORKSPACE_ROOT) {
958
+ return process.env.NX_WORKSPACE_ROOT;
959
+ }
960
+ let currentDir = startDir || process.cwd();
961
+ let lastPackageJsonDir = null;
962
+ while (currentDir !== path2.parse(currentDir).root) {
963
+ if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
964
+ return currentDir;
965
+ }
966
+ if (hasPackageJson(currentDir)) {
967
+ lastPackageJsonDir = currentDir;
968
+ }
969
+ currentDir = path2.dirname(currentDir);
970
+ }
971
+ if (lastPackageJsonDir) {
972
+ return lastPackageJsonDir;
973
+ }
974
+ throw new Error(
975
+ `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.`
976
+ );
977
+ }
978
+
979
+ // src/config/microfrontends/utils/get-application-context.ts
980
+ import fs4 from "node:fs";
981
+ import path3 from "node:path";
982
+ function getApplicationContext(opts) {
983
+ if (opts?.appName) {
984
+ logger.debug(
985
+ "[MFE Config] Application name from appName parameter:",
986
+ opts.appName
987
+ );
988
+ return { name: opts.appName };
989
+ }
990
+ if (process.env.VERCEL_PROJECT_NAME) {
991
+ logger.debug(
992
+ "[MFE Config] Application name from VERCEL_PROJECT_NAME:",
993
+ process.env.VERCEL_PROJECT_NAME
994
+ );
995
+ return {
996
+ name: process.env.VERCEL_PROJECT_NAME,
997
+ projectName: process.env.VERCEL_PROJECT_NAME
998
+ };
999
+ }
1000
+ if (process.env.NX_TASK_TARGET_PROJECT) {
1001
+ logger.debug(
1002
+ "[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
1003
+ process.env.NX_TASK_TARGET_PROJECT
1004
+ );
1005
+ return {
1006
+ name: process.env.NX_TASK_TARGET_PROJECT,
1007
+ packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1008
+ };
1009
+ }
1010
+ try {
1011
+ const vercelProjectJsonPath = fs4.readFileSync(
1012
+ path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1013
+ "utf-8"
1014
+ );
1015
+ const projectJson = JSON.parse(vercelProjectJsonPath);
1016
+ if (projectJson.projectName) {
1017
+ logger.debug(
1018
+ "[MFE Config] Application name from .vercel/project.json:",
1019
+ projectJson.projectName
1020
+ );
1021
+ return {
1022
+ name: projectJson.projectName,
1023
+ projectName: projectJson.projectName
1024
+ };
1025
+ }
1026
+ } catch (_) {
1027
+ }
1028
+ try {
1029
+ const packageJsonString = fs4.readFileSync(
1030
+ path3.join(opts?.packageRoot || ".", "package.json"),
1031
+ "utf-8"
1032
+ );
1033
+ const packageJson = JSON.parse(packageJsonString);
1034
+ if (!packageJson.name) {
1035
+ throw new MicrofrontendError(
1036
+ `package.json file missing required field "name"`,
1037
+ {
1038
+ type: "packageJson",
1039
+ subtype: "missing_field_name",
1040
+ source: "@vercel/microfrontends/next"
1041
+ }
1042
+ );
1043
+ }
1044
+ logger.debug(
1045
+ "[MFE Config] Application name from package.json:",
1046
+ packageJson.name
1047
+ );
1048
+ return { name: packageJson.name, packageJsonName: packageJson.name };
1049
+ } catch (err) {
1050
+ throw MicrofrontendError.handle(err, {
1051
+ fileName: "package.json"
1052
+ });
1053
+ }
1054
+ }
1055
+
1056
+ // src/config/microfrontends/utils/infer-microfrontends-location.ts
1057
+ import { readFileSync, statSync } from "node:fs";
1058
+ import { dirname, join as join2 } from "node:path";
1059
+ import fg from "fast-glob";
1060
+ import { parse as parse2 } from "jsonc-parser";
1061
+ var configCache = {};
1062
+ function findPackageWithMicrofrontendsConfig({
1063
+ repositoryRoot,
1064
+ applicationContext,
1065
+ customConfigFilename
1066
+ }) {
1067
+ const applicationName = applicationContext.name;
1068
+ logger.debug(
1069
+ "[MFE Config] Searching repository for configs containing application:",
1070
+ applicationName
1071
+ );
1072
+ try {
1073
+ const microfrontendsJsonPaths = fg.globSync(
1074
+ `**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
1075
+ {
1076
+ cwd: repositoryRoot,
1077
+ absolute: true,
1078
+ onlyFiles: true,
1079
+ followSymbolicLinks: false,
1080
+ ignore: ["**/node_modules/**", "**/.git/**"]
1081
+ }
1082
+ );
1083
+ logger.debug(
1084
+ "[MFE Config] Found",
1085
+ microfrontendsJsonPaths.length,
1086
+ "config file(s) in repository"
1087
+ );
1088
+ const matchingPaths = [];
1089
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
1090
+ if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
1091
+ matchingPaths.push(microfrontendsJsonPath);
1092
+ }
1093
+ }
1094
+ logger.debug(
1095
+ "[MFE Config] Total matching config files:",
1096
+ matchingPaths.length
1097
+ );
1098
+ if (matchingPaths.length > 1) {
1099
+ throw new MicrofrontendError(
1100
+ `Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
1101
+ ${matchingPaths.join("\n \u2022 ")}`,
1102
+ { type: "config", subtype: "inference_failed" }
1103
+ );
1104
+ }
1105
+ if (matchingPaths.length === 0) {
1106
+ if (repositoryRoot && doesMisplacedConfigExist(
1107
+ repositoryRoot,
1108
+ applicationName,
1109
+ customConfigFilename
1110
+ )) {
1111
+ logger.debug(
1112
+ "[MFE Config] Found misplaced config in wrong .vercel directory in repository"
1113
+ );
1114
+ const misplacedConfigPath = join2(
1115
+ repositoryRoot,
1116
+ ".vercel",
1117
+ customConfigFilename || "microfrontends.json"
1118
+ );
1119
+ throw new MicrofrontendError(
1120
+ `Unable to automatically infer the location of the \`microfrontends.json\` file.
1121
+
1122
+ A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
1123
+ However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
1124
+
1125
+ To fix this:
1126
+ 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
1127
+ 2. If manually defined, move the config file to the \`.vercel\` directory in your application
1128
+ 3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
1129
+
1130
+ For more information, see: https://vercel.com/docs/cli/project-linking`,
1131
+ { type: "config", subtype: "inference_failed" }
1132
+ );
1133
+ }
1134
+ let additionalErrorMessage = "";
1135
+ if (microfrontendsJsonPaths.length > 0) {
1136
+ if (!applicationContext.projectName) {
1137
+ additionalErrorMessage = `
1138
+
1139
+ 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.`;
1140
+ } else {
1141
+ additionalErrorMessage = `
1142
+
1143
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1144
+ }
1145
+ }
1146
+ throw new MicrofrontendError(
1147
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1087
1148
 
1088
- // src/config/microfrontends/utils/get-application-context.ts
1089
- import fs5 from "node:fs";
1090
- import path4 from "node:path";
1091
- function getApplicationContext(opts) {
1092
- if (opts?.appName) {
1093
- return { name: opts.appName };
1149
+ 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.
1150
+
1151
+ 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.
1152
+
1153
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1154
+ { type: "config", subtype: "inference_failed" }
1155
+ );
1156
+ }
1157
+ const [packageJsonPath] = matchingPaths;
1158
+ return dirname(packageJsonPath);
1159
+ } catch (error2) {
1160
+ if (error2 instanceof MicrofrontendError) {
1161
+ throw error2;
1162
+ }
1163
+ return null;
1094
1164
  }
1095
- if (process.env.VERCEL_PROJECT_NAME) {
1096
- return {
1097
- name: process.env.VERCEL_PROJECT_NAME,
1098
- projectName: process.env.VERCEL_PROJECT_NAME
1099
- };
1165
+ }
1166
+ function inferMicrofrontendsLocation(opts) {
1167
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
1168
+ if (configCache[cacheKey]) {
1169
+ return configCache[cacheKey];
1100
1170
  }
1101
- if (process.env.NX_TASK_TARGET_PROJECT) {
1102
- return {
1103
- name: process.env.NX_TASK_TARGET_PROJECT,
1104
- packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1105
- };
1171
+ const result = findPackageWithMicrofrontendsConfig(opts);
1172
+ if (!result) {
1173
+ throw new MicrofrontendError(
1174
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1175
+ { type: "config", subtype: "inference_failed" }
1176
+ );
1106
1177
  }
1178
+ configCache[cacheKey] = result;
1179
+ return result;
1180
+ }
1181
+ function existsSync(path6) {
1107
1182
  try {
1108
- const vercelProjectJsonPath = fs5.readFileSync(
1109
- path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1110
- "utf-8"
1111
- );
1112
- const projectJson = JSON.parse(vercelProjectJsonPath);
1113
- if (projectJson.projectName) {
1114
- return {
1115
- name: projectJson.projectName,
1116
- projectName: projectJson.projectName
1117
- };
1118
- }
1183
+ statSync(path6);
1184
+ return true;
1119
1185
  } catch (_) {
1186
+ return false;
1120
1187
  }
1188
+ }
1189
+ function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
1190
+ logger.debug(
1191
+ "[MFE Config] Looking for misplaced config in wrong .vercel directory"
1192
+ );
1193
+ const misplacedConfigPath = join2(
1194
+ repositoryRoot,
1195
+ ".vercel",
1196
+ customConfigFilename || "microfrontends.json"
1197
+ );
1198
+ return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
1199
+ }
1200
+ function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
1121
1201
  try {
1122
- const packageJsonString = fs5.readFileSync(
1123
- path4.join(opts?.packageRoot || ".", "package.json"),
1202
+ const microfrontendsJsonContent = readFileSync(
1203
+ microfrontendsJsonPath,
1124
1204
  "utf-8"
1125
1205
  );
1126
- const packageJson = JSON.parse(packageJsonString);
1127
- if (!packageJson.name) {
1128
- throw new MicrofrontendError(
1129
- `package.json file missing required field "name"`,
1130
- {
1131
- type: "packageJson",
1132
- subtype: "missing_field_name",
1133
- source: "@vercel/microfrontends/next"
1134
- }
1206
+ const microfrontendsJson = parse2(microfrontendsJsonContent);
1207
+ if (microfrontendsJson.applications[applicationName]) {
1208
+ logger.debug(
1209
+ "[MFE Config] Found application in config:",
1210
+ microfrontendsJsonPath
1135
1211
  );
1212
+ return true;
1136
1213
  }
1137
- return { name: packageJson.name, packageJsonName: packageJson.name };
1138
- } catch (err) {
1139
- throw MicrofrontendError.handle(err, {
1140
- fileName: "package.json"
1141
- });
1214
+ for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
1215
+ if (app.packageName === applicationName) {
1216
+ logger.debug(
1217
+ "[MFE Config] Found application via packageName in config:",
1218
+ microfrontendsJsonPath
1219
+ );
1220
+ return true;
1221
+ }
1222
+ }
1223
+ } catch (error2) {
1224
+ logger.debug("[MFE Config] Error checking application in config:", error2);
1225
+ }
1226
+ return false;
1227
+ }
1228
+
1229
+ // src/config/microfrontends/utils/is-monorepo.ts
1230
+ import fs5 from "node:fs";
1231
+ import path4 from "node:path";
1232
+ function isMonorepo({
1233
+ repositoryRoot
1234
+ }) {
1235
+ try {
1236
+ if (fs5.existsSync(path4.join(repositoryRoot, "pnpm-workspace.yaml"))) {
1237
+ return true;
1238
+ }
1239
+ if (fs5.existsSync(path4.join(repositoryRoot, "vlt-workspaces.json"))) {
1240
+ return true;
1241
+ }
1242
+ if (process.env.NX_WORKSPACE_ROOT === path4.resolve(repositoryRoot)) {
1243
+ return true;
1244
+ }
1245
+ const packageJsonPath = path4.join(repositoryRoot, "package.json");
1246
+ if (!fs5.existsSync(packageJsonPath)) {
1247
+ return false;
1248
+ }
1249
+ const packageJson = JSON.parse(
1250
+ fs5.readFileSync(packageJsonPath, "utf-8")
1251
+ );
1252
+ return packageJson.workspaces !== void 0;
1253
+ } catch (error2) {
1254
+ logger.error("Error determining if repository is a monorepo", error2);
1255
+ return false;
1142
1256
  }
1143
1257
  }
1144
1258
 
@@ -1155,8 +1269,8 @@ function getOutputFilePath() {
1155
1269
  }
1156
1270
 
1157
1271
  // src/config/microfrontends/server/validation.ts
1158
- import { parse as parse3 } from "jsonc-parser";
1159
1272
  import { Ajv } from "ajv";
1273
+ import { parse as parse3 } from "jsonc-parser";
1160
1274
 
1161
1275
  // schema/schema.json
1162
1276
  var schema_default = {
@@ -1184,9 +1298,7 @@ var schema_default = {
1184
1298
  description: "Optional configuration options for the microfrontend."
1185
1299
  }
1186
1300
  },
1187
- required: [
1188
- "applications"
1189
- ],
1301
+ required: ["applications"],
1190
1302
  additionalProperties: false,
1191
1303
  description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
1192
1304
  },
@@ -1223,19 +1335,14 @@ var schema_default = {
1223
1335
  description: "Development configuration for the default application."
1224
1336
  }
1225
1337
  },
1226
- required: [
1227
- "development"
1228
- ],
1338
+ required: ["development"],
1229
1339
  additionalProperties: false
1230
1340
  },
1231
1341
  DefaultDevelopment: {
1232
1342
  type: "object",
1233
1343
  properties: {
1234
1344
  local: {
1235
- type: [
1236
- "number",
1237
- "string"
1238
- ],
1345
+ type: ["number", "string"],
1239
1346
  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."
1240
1347
  },
1241
1348
  task: {
@@ -1247,9 +1354,7 @@ var schema_default = {
1247
1354
  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."
1248
1355
  }
1249
1356
  },
1250
- required: [
1251
- "fallback"
1252
- ],
1357
+ required: ["fallback"],
1253
1358
  additionalProperties: false
1254
1359
  },
1255
1360
  ChildApplication: {
@@ -1272,19 +1377,14 @@ var schema_default = {
1272
1377
  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."
1273
1378
  }
1274
1379
  },
1275
- required: [
1276
- "routing"
1277
- ],
1380
+ required: ["routing"],
1278
1381
  additionalProperties: false
1279
1382
  },
1280
1383
  ChildDevelopment: {
1281
1384
  type: "object",
1282
1385
  properties: {
1283
1386
  local: {
1284
- type: [
1285
- "number",
1286
- "string"
1287
- ],
1387
+ type: ["number", "string"],
1288
1388
  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."
1289
1389
  },
1290
1390
  task: {
@@ -1324,9 +1424,7 @@ var schema_default = {
1324
1424
  description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
1325
1425
  }
1326
1426
  },
1327
- required: [
1328
- "paths"
1329
- ],
1427
+ required: ["paths"],
1330
1428
  additionalProperties: false,
1331
1429
  description: "A group of paths that is routed to this application."
1332
1430
  },
@@ -1505,7 +1603,13 @@ var MicrofrontendsServer = class {
1505
1603
  filePath,
1506
1604
  cookies
1507
1605
  } = {}) {
1606
+ logger.debug("[MFE Config] Starting config inference", {
1607
+ appName,
1608
+ directory: directory || process.cwd(),
1609
+ filePath
1610
+ });
1508
1611
  if (filePath) {
1612
+ logger.debug("[MFE Config] Using explicit filePath:", filePath);
1509
1613
  return MicrofrontendsServer.fromFile({
1510
1614
  filePath,
1511
1615
  cookies
@@ -1513,16 +1617,25 @@ var MicrofrontendsServer = class {
1513
1617
  }
1514
1618
  try {
1515
1619
  const packageRoot = findPackageRoot(directory);
1620
+ logger.debug("[MFE Config] Package root:", packageRoot);
1516
1621
  const applicationContext = getApplicationContext({
1517
1622
  appName,
1518
1623
  packageRoot
1519
1624
  });
1625
+ logger.debug("[MFE Config] Application context:", applicationContext);
1520
1626
  const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
1627
+ if (customConfigFilename) {
1628
+ logger.debug(
1629
+ "[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
1630
+ customConfigFilename
1631
+ );
1632
+ }
1521
1633
  const maybeConfig = findConfig({
1522
1634
  dir: packageRoot,
1523
1635
  customConfigFilename
1524
1636
  });
1525
1637
  if (maybeConfig) {
1638
+ logger.debug("[MFE Config] Config found at package root:", maybeConfig);
1526
1639
  return MicrofrontendsServer.fromFile({
1527
1640
  filePath: maybeConfig,
1528
1641
  cookies
@@ -1530,42 +1643,78 @@ var MicrofrontendsServer = class {
1530
1643
  }
1531
1644
  const repositoryRoot = findRepositoryRoot();
1532
1645
  const isMonorepo2 = isMonorepo({ repositoryRoot });
1646
+ logger.debug(
1647
+ "[MFE Config] Repository root:",
1648
+ repositoryRoot,
1649
+ "Is monorepo:",
1650
+ isMonorepo2
1651
+ );
1533
1652
  const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
1534
1653
  if (typeof configFromEnv === "string") {
1654
+ logger.debug(
1655
+ "[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
1656
+ configFromEnv
1657
+ );
1535
1658
  const maybeConfigFromEnv = resolve(packageRoot, configFromEnv);
1536
1659
  if (maybeConfigFromEnv) {
1660
+ logger.debug(
1661
+ "[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
1662
+ maybeConfigFromEnv
1663
+ );
1537
1664
  return MicrofrontendsServer.fromFile({
1538
1665
  filePath: maybeConfigFromEnv,
1539
1666
  cookies
1540
1667
  });
1541
1668
  }
1542
1669
  } else {
1670
+ const vercelDir = join3(packageRoot, ".vercel");
1671
+ logger.debug(
1672
+ "[MFE Config] Searching for config in .vercel directory:",
1673
+ vercelDir
1674
+ );
1543
1675
  const maybeConfigFromVercel = findConfig({
1544
- dir: join2(packageRoot, ".vercel"),
1676
+ dir: vercelDir,
1545
1677
  customConfigFilename
1546
1678
  });
1547
1679
  if (maybeConfigFromVercel) {
1680
+ logger.debug(
1681
+ "[MFE Config] Config found in .vercel directory:",
1682
+ maybeConfigFromVercel
1683
+ );
1548
1684
  return MicrofrontendsServer.fromFile({
1549
1685
  filePath: maybeConfigFromVercel,
1550
1686
  cookies
1551
1687
  });
1552
1688
  }
1553
1689
  if (isMonorepo2) {
1690
+ logger.debug(
1691
+ "[MFE Config] Inferring microfrontends location in monorepo for application:",
1692
+ applicationContext.name
1693
+ );
1554
1694
  const defaultPackage = inferMicrofrontendsLocation({
1555
1695
  repositoryRoot,
1556
1696
  applicationContext,
1557
1697
  customConfigFilename
1558
1698
  });
1699
+ logger.debug(
1700
+ "[MFE Config] Inferred package location:",
1701
+ defaultPackage
1702
+ );
1559
1703
  const maybeConfigFromDefault = findConfig({
1560
1704
  dir: defaultPackage,
1561
1705
  customConfigFilename
1562
1706
  });
1563
1707
  if (maybeConfigFromDefault) {
1708
+ logger.debug(
1709
+ "[MFE Config] Config found in inferred package:",
1710
+ maybeConfigFromDefault
1711
+ );
1564
1712
  return MicrofrontendsServer.fromFile({
1565
1713
  filePath: maybeConfigFromDefault,
1566
1714
  cookies
1567
1715
  });
1568
1716
  }
1717
+ logger.debug("[MFE Config] No config found in inferred package");
1569
1718
  }
1570
1719
  }
1571
1720
  throw new MicrofrontendError(
@@ -1591,8 +1740,13 @@ var MicrofrontendsServer = class {
1591
1740
  cookies
1592
1741
  }) {
1593
1742
  try {
1743
+ logger.debug("[MFE Config] Reading config from file:", filePath);
1594
1744
  const configJson = fs6.readFileSync(filePath, "utf-8");
1595
1745
  const config = MicrofrontendsServer.validate(configJson);
1746
+ logger.debug(
1747
+ "[MFE Config] Config loaded with applications:",
1748
+ Object.keys(config.applications)
1749
+ );
1596
1750
  return new MicrofrontendsServer({
1597
1751
  config,
1598
1752
  overrides: cookies ? parseOverrides(cookies) : void 0
@@ -1632,6 +1786,51 @@ var MicrofrontendsServer = class {
1632
1786
  }
1633
1787
  };
1634
1788
 
1789
+ // src/next/config/env.ts
1790
+ function debugEnv(env) {
1791
+ const indent = " ".repeat(4);
1792
+ const header = "env (key \u2192 val)";
1793
+ const separator = "\u23AF".repeat(header.length);
1794
+ const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
1795
+ const table = Object.keys(env).map((key, idx) => {
1796
+ const paddedKey = key.padEnd(maxKeyLength);
1797
+ return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
1798
+ }).join("\n");
1799
+ logger.debug(`${indent}${header}
1800
+ ${indent}${separator}
1801
+ ${table}
1802
+ `);
1803
+ }
1804
+ function setEnvironment({
1805
+ app,
1806
+ microfrontends
1807
+ }) {
1808
+ const clientEnvs = {
1809
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1810
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
1811
+ NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
1812
+ microfrontends.config.toClientConfig({
1813
+ removeFlaggedPaths: true
1814
+ }).serialize()
1815
+ ),
1816
+ ...app.getAssetPrefix() ? {
1817
+ NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
1818
+ } : {},
1819
+ ...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
1820
+ NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
1821
+ } : {}
1822
+ };
1823
+ const serverEnvs = {
1824
+ MFE_CURRENT_APPLICATION: app.name,
1825
+ MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1826
+ };
1827
+ const allEnvs = { ...clientEnvs, ...serverEnvs };
1828
+ for (const [key, value] of Object.entries(allEnvs)) {
1829
+ process.env[key] = value;
1830
+ }
1831
+ debugEnv(allEnvs);
1832
+ }
1833
+
1635
1834
  // src/next/config/transforms/asset-prefix.ts
1636
1835
  function transform(args) {
1637
1836
  const { next, app } = args;
@@ -1736,20 +1935,6 @@ ${indent} - Automatically redirecting all requests to local microfrontends proxy
1736
1935
  return { next };
1737
1936
  }
1738
1937
 
1739
- // src/next/config/transforms/transpile-packages.ts
1740
- function transform5(args) {
1741
- const { next } = args;
1742
- if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
1743
- next.transpilePackages = [
1744
- ...next.transpilePackages || [],
1745
- "@vercel/microfrontends"
1746
- ];
1747
- }
1748
- return {
1749
- next
1750
- };
1751
- }
1752
-
1753
1938
  // src/next/config/transforms/rewrites.ts
1754
1939
  function debugRewrites(rewrites) {
1755
1940
  const indent = " ".repeat(4);
@@ -1778,7 +1963,7 @@ function rewritesMapToArr(rewrites) {
1778
1963
  ];
1779
1964
  });
1780
1965
  }
1781
- function transform6(args) {
1966
+ function transform5(args) {
1782
1967
  const { app, next } = args;
1783
1968
  const buildBeforeFiles = () => {
1784
1969
  const rewrites = /* @__PURE__ */ new Map();
@@ -1841,6 +2026,20 @@ function transform6(args) {
1841
2026
  };
1842
2027
  }
1843
2028
 
2029
+ // src/next/config/transforms/transpile-packages.ts
2030
+ function transform6(args) {
2031
+ const { next } = args;
2032
+ if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
2033
+ next.transpilePackages = [
2034
+ ...next.transpilePackages || [],
2035
+ "@vercel/microfrontends"
2036
+ ];
2037
+ }
2038
+ return {
2039
+ next
2040
+ };
2041
+ }
2042
+
1844
2043
  // src/next/config/transforms/webpack.ts
1845
2044
  import fs7 from "node:fs";
1846
2045
  import { createRequire } from "node:module";
@@ -1873,8 +2072,12 @@ var nextVersion = getNextJsVersion();
1873
2072
  function transform7(args) {
1874
2073
  const useDefineServer = args.opts?.preferWebpackEnvironmentPlugin ? false : semver.gte(nextVersion, "15.4.0-canary.41");
1875
2074
  const { next, microfrontend, opts } = args;
2075
+ const isNext16OrHigher = semver.gte(nextVersion, "16.0.0");
2076
+ const hasTurbopackConfig = Boolean(next.turbopack);
2077
+ const turbopackConfig = isNext16OrHigher && !hasTurbopackConfig ? { turbopack: {} } : {};
1876
2078
  const configWithWebpack = {
1877
2079
  ...next,
2080
+ ...turbopackConfig,
1878
2081
  ...useDefineServer ? {
1879
2082
  compiler: {
1880
2083
  ...next.compiler,
@@ -1954,56 +2157,11 @@ var transforms = {
1954
2157
  buildId: transform2,
1955
2158
  draftMode: transform3,
1956
2159
  redirects: transform4,
1957
- rewrites: transform6,
1958
- transpilePackages: transform5,
2160
+ rewrites: transform5,
2161
+ transpilePackages: transform6,
1959
2162
  webpack: transform7
1960
2163
  };
1961
2164
 
1962
- // src/next/config/env.ts
1963
- function debugEnv(env) {
1964
- const indent = " ".repeat(4);
1965
- const header = "env (key \u2192 val)";
1966
- const separator = "\u23AF".repeat(header.length);
1967
- const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
1968
- const table = Object.keys(env).map((key, idx) => {
1969
- const paddedKey = key.padEnd(maxKeyLength);
1970
- return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
1971
- }).join("\n");
1972
- logger.debug(`${indent}${header}
1973
- ${indent}${separator}
1974
- ${table}
1975
- `);
1976
- }
1977
- function setEnvironment({
1978
- app,
1979
- microfrontends
1980
- }) {
1981
- const clientEnvs = {
1982
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1983
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
1984
- NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
1985
- microfrontends.config.toClientConfig({
1986
- removeFlaggedPaths: true
1987
- }).serialize()
1988
- ),
1989
- ...app.getAssetPrefix() ? {
1990
- NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
1991
- } : {},
1992
- ...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
1993
- NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
1994
- } : {}
1995
- };
1996
- const serverEnvs = {
1997
- MFE_CURRENT_APPLICATION: app.name,
1998
- MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1999
- };
2000
- const allEnvs = { ...clientEnvs, ...serverEnvs };
2001
- for (const [key, value] of Object.entries(allEnvs)) {
2002
- process.env[key] = value;
2003
- }
2004
- debugEnv(allEnvs);
2005
- }
2006
-
2007
2165
  // src/next/config/index.ts
2008
2166
  function typedEntries(obj) {
2009
2167
  return Object.entries(obj);