@vercel/microfrontends 0.18.0 → 0.19.1

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 (47) hide show
  1. package/dist/bin/cli.cjs +151 -71
  2. package/dist/config.cjs +102 -56
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +4 -4
  5. package/dist/config.js +102 -56
  6. package/dist/config.js.map +1 -1
  7. package/dist/{index-24024799.d.ts → index-09b1ddf9.d.ts} +16 -19
  8. package/dist/{index-ef8657e6.d.ts → index-2f78c0ca.d.ts} +44 -7
  9. package/dist/microfrontends/server.cjs +147 -67
  10. package/dist/microfrontends/server.cjs.map +1 -1
  11. package/dist/microfrontends/server.d.ts +4 -4
  12. package/dist/microfrontends/server.js +147 -67
  13. package/dist/microfrontends/server.js.map +1 -1
  14. package/dist/microfrontends.cjs +103 -57
  15. package/dist/microfrontends.cjs.map +1 -1
  16. package/dist/microfrontends.d.ts +4 -4
  17. package/dist/microfrontends.js +103 -57
  18. package/dist/microfrontends.js.map +1 -1
  19. package/dist/next/config.cjs +166 -79
  20. package/dist/next/config.cjs.map +1 -1
  21. package/dist/next/config.js +166 -79
  22. package/dist/next/config.js.map +1 -1
  23. package/dist/next/endpoints.d.ts +2 -2
  24. package/dist/next/middleware.cjs +120 -75
  25. package/dist/next/middleware.cjs.map +1 -1
  26. package/dist/next/middleware.js +120 -75
  27. package/dist/next/middleware.js.map +1 -1
  28. package/dist/next/testing.cjs +109 -62
  29. package/dist/next/testing.cjs.map +1 -1
  30. package/dist/next/testing.d.ts +4 -4
  31. package/dist/next/testing.js +109 -62
  32. package/dist/next/testing.js.map +1 -1
  33. package/dist/overrides.d.ts +3 -3
  34. package/dist/schema.d.ts +1 -1
  35. package/dist/{types-089498fd.d.ts → types-4ef2bddb.d.ts} +1 -1
  36. package/dist/{types-9f161cec.d.ts → types-b6d38aea.d.ts} +1 -1
  37. package/dist/utils/mfe-port.cjs +148 -68
  38. package/dist/utils/mfe-port.cjs.map +1 -1
  39. package/dist/utils/mfe-port.js +148 -68
  40. package/dist/utils/mfe-port.js.map +1 -1
  41. package/dist/validation.cjs +43 -9
  42. package/dist/validation.cjs.map +1 -1
  43. package/dist/validation.d.ts +1 -1
  44. package/dist/validation.js +43 -9
  45. package/dist/validation.js.map +1 -1
  46. package/package.json +1 -1
  47. package/schema/schema.json +71 -40
@@ -234,7 +234,8 @@ var validateConfigPaths = (applicationConfigsById) => {
234
234
  if (isDefaultApp(app)) {
235
235
  continue;
236
236
  }
237
- for (const pathMatch of app.routing) {
237
+ const childApp = app;
238
+ for (const pathMatch of childApp.routing) {
238
239
  for (const path6 of pathMatch.paths) {
239
240
  const maybeError = validatePathExpression(path6);
240
241
  if (maybeError) {
@@ -254,33 +255,31 @@ var validateConfigPaths = (applicationConfigsById) => {
254
255
  }
255
256
  }
256
257
  const entries = Array.from(pathsByApplicationId.entries());
257
- entries.forEach(([path6, { applications: ids, matcher, applicationId }]) => {
258
+ for (const [path6, { applications: ids, matcher, applicationId }] of entries) {
258
259
  if (ids.length > 1) {
259
260
  errors.push(
260
261
  `Duplicate path "${path6}" for applications "${ids.join(", ")}"`
261
262
  );
262
263
  }
263
- entries.forEach(
264
- ([
265
- matchPath,
266
- { applications: matchIds, applicationId: matchApplicationId }
267
- ]) => {
268
- if (path6 === matchPath) {
269
- return;
270
- }
271
- if (applicationId === matchApplicationId) {
272
- return;
273
- }
274
- if (matcher.test(matchPath)) {
275
- const source = `"${path6}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
276
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
277
- errors.push(
278
- `Overlapping path detected between ${source} and ${destination}`
279
- );
280
- }
264
+ for (const [
265
+ matchPath,
266
+ { applications: matchIds, applicationId: matchApplicationId }
267
+ ] of entries) {
268
+ if (path6 === matchPath) {
269
+ continue;
281
270
  }
282
- );
283
- });
271
+ if (applicationId === matchApplicationId) {
272
+ continue;
273
+ }
274
+ if (matcher.test(matchPath)) {
275
+ const source = `"${path6}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
276
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
277
+ errors.push(
278
+ `Overlapping path detected between ${source} and ${destination}`
279
+ );
280
+ }
281
+ }
282
+ }
284
283
  if (errors.length) {
285
284
  throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
286
285
  type: "config",
@@ -349,7 +348,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
349
348
  const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
350
349
  if (numApplicationsWithoutRouting === 0) {
351
350
  throw new MicrofrontendError(
352
- `No default application found. At least one application needs to be the default by omitting routing.`,
351
+ "No default application found. At least one application needs to be the default by omitting routing.",
353
352
  { type: "config", subtype: "no_default_application" }
354
353
  );
355
354
  }
@@ -395,47 +394,80 @@ function generatePortFromName({
395
394
  // src/config/microfrontends-config/isomorphic/host.ts
396
395
  var Host = class {
397
396
  constructor(hostConfig, options) {
398
- const { protocol = "https", host, port } = hostConfig;
399
- this.protocol = protocol;
400
- this.host = host;
401
- this.port = Host.getPort({ port, protocol: this.protocol });
397
+ if (typeof hostConfig === "string") {
398
+ ({
399
+ protocol: this.protocol,
400
+ host: this.host,
401
+ port: this.port
402
+ } = Host.parseUrl(hostConfig));
403
+ } else {
404
+ const { protocol = "https", host, port } = hostConfig;
405
+ this.protocol = protocol;
406
+ this.host = host;
407
+ this.port = port;
408
+ }
402
409
  this.local = options?.isLocal;
403
410
  }
404
- isLocal() {
405
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
406
- }
407
- static getPort({
408
- protocol,
409
- port
410
- }) {
411
- if (!port) {
412
- if (protocol === "http") {
413
- return 80;
414
- }
415
- return 443;
411
+ static parseUrl(url) {
412
+ let hostToParse = url;
413
+ if (!/^https?:\/\//.exec(hostToParse)) {
414
+ hostToParse = `https://${hostToParse}`;
415
+ }
416
+ const parsed = new URL(hostToParse);
417
+ if (!parsed.hostname) {
418
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
419
+ }
420
+ if (parsed.hash) {
421
+ throw new Error(
422
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
423
+ );
424
+ }
425
+ if (parsed.username || parsed.password) {
426
+ throw new Error(
427
+ Host.getMicrofrontendsError(
428
+ url,
429
+ "cannot have authentication credentials (username and/or password)"
430
+ )
431
+ );
432
+ }
433
+ if (parsed.pathname !== "/") {
434
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
435
+ }
436
+ if (parsed.search) {
437
+ throw new Error(
438
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
439
+ );
416
440
  }
417
- return port;
441
+ const protocol = parsed.protocol.slice(0, -1);
442
+ return {
443
+ protocol,
444
+ host: parsed.hostname,
445
+ port: parsed.port ? Number.parseInt(parsed.port) : void 0
446
+ };
418
447
  }
419
- isDefaultPort() {
420
- return this.port === Host.getPort({ protocol: this.protocol });
448
+ static getMicrofrontendsError(url, message) {
449
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
450
+ }
451
+ isLocal() {
452
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
421
453
  }
422
- toString(opts = {}) {
423
- const url = this.toUrl(opts);
454
+ toString() {
455
+ const url = this.toUrl();
424
456
  return url.toString().replace(/\/$/, "");
425
457
  }
426
- toUrl(opts = {}) {
427
- const { includeDefaultPort } = opts;
428
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
458
+ toUrl() {
459
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
429
460
  return new URL(url);
430
461
  }
431
462
  };
432
463
  var LocalHost = class extends Host {
433
464
  constructor({
434
465
  appName,
466
+ localPort,
435
467
  ...hostConfig
436
468
  }) {
437
469
  const host = hostConfig.host ?? "localhost";
438
- const port = hostConfig.port ?? generatePortFromName({ name: appName });
470
+ const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
439
471
  const protocol = hostConfig.protocol ?? "http";
440
472
  super({ protocol, host, port });
441
473
  }
@@ -452,12 +484,17 @@ var Application = class {
452
484
  this.development = {
453
485
  local: new LocalHost({
454
486
  appName: name,
487
+ localPort: app.development?.localPort,
455
488
  ...app.development?.local
456
489
  }),
457
490
  fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
458
491
  };
459
- this.production = app.production ? new Host(app.production) : void 0;
460
- this.vercel = app.vercel;
492
+ if (app.development?.fallback) {
493
+ this.fallback = new Host(app.development.fallback);
494
+ } else if (app.production) {
495
+ this.fallback = new Host(app.production);
496
+ }
497
+ this.projectId = app.projectId ?? app.vercel?.projectId;
461
498
  this.overrides = overrides?.environment ? {
462
499
  environment: new Host(overrides.environment)
463
500
  } : void 0;
@@ -485,7 +522,16 @@ var DefaultApplication = class extends Application {
485
522
  isDefault: true
486
523
  });
487
524
  this.default = true;
488
- this.production = new Host(app.production);
525
+ const fallbackHost = app.development?.fallback ?? app.production;
526
+ if (fallbackHost === void 0) {
527
+ throw new Error(
528
+ "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
529
+ );
530
+ }
531
+ this.fallback = new Host(fallbackHost);
532
+ if (app.production) {
533
+ this.production = new Host(app.production);
534
+ }
489
535
  }
490
536
  getAssetPrefix() {
491
537
  return "";
@@ -555,7 +601,7 @@ var MicrofrontendConfigIsomorphic = class {
555
601
  }
556
602
  if (isMainConfig(config) && !this.defaultApplication) {
557
603
  throw new MicrofrontendError(
558
- `Could not find default application in microfrontends configuration`,
604
+ "Could not find default application in microfrontends configuration",
559
605
  {
560
606
  type: "application",
561
607
  subtype: "not_found"
@@ -631,11 +677,11 @@ var MicrofrontendConfigIsomorphic = class {
631
677
  return app;
632
678
  }
633
679
  getApplicationByProjectId(projectId) {
634
- if (this.defaultApplication?.vercel?.projectId === projectId) {
680
+ if (this.defaultApplication?.projectId === projectId) {
635
681
  return this.defaultApplication;
636
682
  }
637
683
  return Object.values(this.childApplications).find(
638
- (app) => app.vercel?.projectId === projectId
684
+ (app) => app.projectId === projectId
639
685
  );
640
686
  }
641
687
  /**
@@ -645,7 +691,7 @@ var MicrofrontendConfigIsomorphic = class {
645
691
  getDefaultApplication() {
646
692
  if (!this.defaultApplication) {
647
693
  throw new MicrofrontendError(
648
- `Could not find default application in microfrontends configuration`,
694
+ "Could not find default application in microfrontends configuration",
649
695
  {
650
696
  type: "application",
651
697
  subtype: "not_found"
@@ -658,7 +704,7 @@ var MicrofrontendConfigIsomorphic = class {
658
704
  * Returns the configured port for the local proxy
659
705
  */
660
706
  getLocalProxyPort() {
661
- return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
707
+ return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
662
708
  }
663
709
  /**
664
710
  * Serializes the class back to the Schema type.
@@ -732,7 +778,7 @@ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
732
778
  }
733
779
  if (!defaultApplication) {
734
780
  throw new MicrofrontendError(
735
- `Could not find default application in microfrontends configuration`,
781
+ "Could not find default application in microfrontends configuration",
736
782
  {
737
783
  type: "application",
738
784
  subtype: "not_found"
@@ -1032,11 +1078,21 @@ var schema_default = {
1032
1078
  properties: {
1033
1079
  vercel: {
1034
1080
  $ref: "#/definitions/VercelOptions",
1035
- description: "Micro-Frontends wide options for Vercel."
1081
+ description: "Microfrontends wide options for Vercel.",
1082
+ deprecated: "This is being replaced by the `disableOverrides` field below."
1083
+ },
1084
+ disableOverrides: {
1085
+ type: "boolean",
1086
+ description: "If you want to disable the overrides for the site. For example, if you are managing rewrites between applications externally, you may wish to disable the overrides on the toolbar as they will have no effect."
1036
1087
  },
1037
1088
  localProxy: {
1038
1089
  $ref: "#/definitions/LocalProxyOptions",
1039
- description: "Options for local proxy."
1090
+ description: "Options for local proxy.",
1091
+ deprecated: "This is being replaced by the `localProxyPort` field below."
1092
+ },
1093
+ localProxyPort: {
1094
+ type: "number",
1095
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1040
1096
  }
1041
1097
  },
1042
1098
  additionalProperties: false
@@ -1084,13 +1140,19 @@ var schema_default = {
1084
1140
  type: "object",
1085
1141
  properties: {
1086
1142
  vercel: {
1087
- $ref: "#/definitions/Vercel"
1143
+ $ref: "#/definitions/Vercel",
1144
+ deprecated: "This is being replaced by the `projectId` field below."
1145
+ },
1146
+ projectId: {
1147
+ type: "string",
1148
+ description: "Vercel project ID"
1088
1149
  },
1089
1150
  development: {
1090
1151
  $ref: "#/definitions/Development"
1091
1152
  },
1092
1153
  production: {
1093
- $ref: "#/definitions/HostConfig"
1154
+ $ref: "#/definitions/HostConfig",
1155
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1094
1156
  }
1095
1157
  },
1096
1158
  required: ["production"],
@@ -1111,11 +1173,23 @@ var schema_default = {
1111
1173
  type: "object",
1112
1174
  properties: {
1113
1175
  local: {
1114
- $ref: "#/definitions/LocalHostConfig"
1176
+ $ref: "#/definitions/LocalHostConfig",
1177
+ deprecated: "This is being replaced by the `localPort` field below."
1178
+ },
1179
+ localPort: {
1180
+ type: "number",
1181
+ description: "The local port number that this application runs on when it is running locally. Common values include `80` for HTTP and `443` for HTTPS."
1115
1182
  },
1116
1183
  fallback: {
1117
- $ref: "#/definitions/HostConfig",
1118
- description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error."
1184
+ anyOf: [
1185
+ {
1186
+ $ref: "#/definitions/HostConfig"
1187
+ },
1188
+ {
1189
+ type: "string"
1190
+ }
1191
+ ],
1192
+ description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error.\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."
1119
1193
  },
1120
1194
  task: {
1121
1195
  type: "string",
@@ -1167,7 +1241,12 @@ var schema_default = {
1167
1241
  type: "object",
1168
1242
  properties: {
1169
1243
  vercel: {
1170
- $ref: "#/definitions/Vercel"
1244
+ $ref: "#/definitions/Vercel",
1245
+ deprecated: "This is being replaced by the `projectId` field below."
1246
+ },
1247
+ projectId: {
1248
+ type: "string",
1249
+ description: "Vercel project ID"
1171
1250
  },
1172
1251
  development: {
1173
1252
  $ref: "#/definitions/Development"
@@ -1177,7 +1256,8 @@ var schema_default = {
1177
1256
  description: "Groups of path expressions that are routed to this application."
1178
1257
  },
1179
1258
  production: {
1180
- $ref: "#/definitions/HostConfig"
1259
+ $ref: "#/definitions/HostConfig",
1260
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1181
1261
  }
1182
1262
  },
1183
1263
  required: ["routing"],
@@ -1473,7 +1553,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1473
1553
  const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1474
1554
  if (!defaultApplication) {
1475
1555
  throw new MicrofrontendError(
1476
- `No default application found. At least one application needs to be the default by omitting routing.`,
1556
+ "No default application found. At least one application needs to be the default by omitting routing.",
1477
1557
  { type: "config", subtype: "no_default_application" }
1478
1558
  );
1479
1559
  }
@@ -1526,7 +1606,7 @@ function loadConfig({
1526
1606
  return void 0;
1527
1607
  }
1528
1608
  const app = config.config.getApplication(appName);
1529
- const port = app.development.local.port;
1609
+ const port = app.development.local.port ?? (app.development.local.protocol === "https" ? 443 : 80);
1530
1610
  return { port };
1531
1611
  }
1532
1612
  export {