@vercel/microfrontends 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +164 -79
  20. package/dist/next/config.cjs.map +1 -1
  21. package/dist/next/config.js +164 -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
@@ -230,7 +230,8 @@ var validateConfigPaths = (applicationConfigsById) => {
230
230
  if (isDefaultApp(app)) {
231
231
  continue;
232
232
  }
233
- for (const pathMatch of app.routing) {
233
+ const childApp = app;
234
+ for (const pathMatch of childApp.routing) {
234
235
  for (const path5 of pathMatch.paths) {
235
236
  const maybeError = validatePathExpression(path5);
236
237
  if (maybeError) {
@@ -250,33 +251,31 @@ var validateConfigPaths = (applicationConfigsById) => {
250
251
  }
251
252
  }
252
253
  const entries = Array.from(pathsByApplicationId.entries());
253
- entries.forEach(([path5, { applications: ids, matcher, applicationId }]) => {
254
+ for (const [path5, { applications: ids, matcher, applicationId }] of entries) {
254
255
  if (ids.length > 1) {
255
256
  errors.push(
256
257
  `Duplicate path "${path5}" for applications "${ids.join(", ")}"`
257
258
  );
258
259
  }
259
- entries.forEach(
260
- ([
261
- matchPath,
262
- { applications: matchIds, applicationId: matchApplicationId }
263
- ]) => {
264
- if (path5 === matchPath) {
265
- return;
266
- }
267
- if (applicationId === matchApplicationId) {
268
- return;
269
- }
270
- if (matcher.test(matchPath)) {
271
- const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
272
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
273
- errors.push(
274
- `Overlapping path detected between ${source} and ${destination}`
275
- );
276
- }
260
+ for (const [
261
+ matchPath,
262
+ { applications: matchIds, applicationId: matchApplicationId }
263
+ ] of entries) {
264
+ if (path5 === matchPath) {
265
+ continue;
277
266
  }
278
- );
279
- });
267
+ if (applicationId === matchApplicationId) {
268
+ continue;
269
+ }
270
+ if (matcher.test(matchPath)) {
271
+ const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
272
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
273
+ errors.push(
274
+ `Overlapping path detected between ${source} and ${destination}`
275
+ );
276
+ }
277
+ }
278
+ }
280
279
  if (errors.length) {
281
280
  throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
282
281
  type: "config",
@@ -345,7 +344,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
345
344
  const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
346
345
  if (numApplicationsWithoutRouting === 0) {
347
346
  throw new MicrofrontendError(
348
- `No default application found. At least one application needs to be the default by omitting routing.`,
347
+ "No default application found. At least one application needs to be the default by omitting routing.",
349
348
  { type: "config", subtype: "no_default_application" }
350
349
  );
351
350
  }
@@ -391,47 +390,80 @@ function generatePortFromName({
391
390
  // src/config/microfrontends-config/isomorphic/host.ts
392
391
  var Host = class {
393
392
  constructor(hostConfig, options) {
394
- const { protocol = "https", host, port } = hostConfig;
395
- this.protocol = protocol;
396
- this.host = host;
397
- this.port = Host.getPort({ port, protocol: this.protocol });
393
+ if (typeof hostConfig === "string") {
394
+ ({
395
+ protocol: this.protocol,
396
+ host: this.host,
397
+ port: this.port
398
+ } = Host.parseUrl(hostConfig));
399
+ } else {
400
+ const { protocol = "https", host, port } = hostConfig;
401
+ this.protocol = protocol;
402
+ this.host = host;
403
+ this.port = port;
404
+ }
398
405
  this.local = options?.isLocal;
399
406
  }
400
- isLocal() {
401
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
402
- }
403
- static getPort({
404
- protocol,
405
- port
406
- }) {
407
- if (!port) {
408
- if (protocol === "http") {
409
- return 80;
410
- }
411
- return 443;
407
+ static parseUrl(url) {
408
+ let hostToParse = url;
409
+ if (!/^https?:\/\//.exec(hostToParse)) {
410
+ hostToParse = `https://${hostToParse}`;
411
+ }
412
+ const parsed = new URL(hostToParse);
413
+ if (!parsed.hostname) {
414
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
412
415
  }
413
- return port;
416
+ if (parsed.hash) {
417
+ throw new Error(
418
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
419
+ );
420
+ }
421
+ if (parsed.username || parsed.password) {
422
+ throw new Error(
423
+ Host.getMicrofrontendsError(
424
+ url,
425
+ "cannot have authentication credentials (username and/or password)"
426
+ )
427
+ );
428
+ }
429
+ if (parsed.pathname !== "/") {
430
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
431
+ }
432
+ if (parsed.search) {
433
+ throw new Error(
434
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
435
+ );
436
+ }
437
+ const protocol = parsed.protocol.slice(0, -1);
438
+ return {
439
+ protocol,
440
+ host: parsed.hostname,
441
+ port: parsed.port ? Number.parseInt(parsed.port) : void 0
442
+ };
443
+ }
444
+ static getMicrofrontendsError(url, message) {
445
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
414
446
  }
415
- isDefaultPort() {
416
- return this.port === Host.getPort({ protocol: this.protocol });
447
+ isLocal() {
448
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
417
449
  }
418
- toString(opts = {}) {
419
- const url = this.toUrl(opts);
450
+ toString() {
451
+ const url = this.toUrl();
420
452
  return url.toString().replace(/\/$/, "");
421
453
  }
422
- toUrl(opts = {}) {
423
- const { includeDefaultPort } = opts;
424
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
454
+ toUrl() {
455
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
425
456
  return new URL(url);
426
457
  }
427
458
  };
428
459
  var LocalHost = class extends Host {
429
460
  constructor({
430
461
  appName,
462
+ localPort,
431
463
  ...hostConfig
432
464
  }) {
433
465
  const host = hostConfig.host ?? "localhost";
434
- const port = hostConfig.port ?? generatePortFromName({ name: appName });
466
+ const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
435
467
  const protocol = hostConfig.protocol ?? "http";
436
468
  super({ protocol, host, port });
437
469
  }
@@ -448,12 +480,17 @@ var Application = class {
448
480
  this.development = {
449
481
  local: new LocalHost({
450
482
  appName: name,
483
+ localPort: app.development?.localPort,
451
484
  ...app.development?.local
452
485
  }),
453
486
  fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
454
487
  };
455
- this.production = app.production ? new Host(app.production) : void 0;
456
- this.vercel = app.vercel;
488
+ if (app.development?.fallback) {
489
+ this.fallback = new Host(app.development.fallback);
490
+ } else if (app.production) {
491
+ this.fallback = new Host(app.production);
492
+ }
493
+ this.projectId = app.projectId ?? app.vercel?.projectId;
457
494
  this.overrides = overrides?.environment ? {
458
495
  environment: new Host(overrides.environment)
459
496
  } : void 0;
@@ -481,7 +518,16 @@ var DefaultApplication = class extends Application {
481
518
  isDefault: true
482
519
  });
483
520
  this.default = true;
484
- this.production = new Host(app.production);
521
+ const fallbackHost = app.development?.fallback ?? app.production;
522
+ if (fallbackHost === void 0) {
523
+ throw new Error(
524
+ "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
525
+ );
526
+ }
527
+ this.fallback = new Host(fallbackHost);
528
+ if (app.production) {
529
+ this.production = new Host(app.production);
530
+ }
485
531
  }
486
532
  getAssetPrefix() {
487
533
  return "";
@@ -551,7 +597,7 @@ var MicrofrontendConfigIsomorphic = class {
551
597
  }
552
598
  if (isMainConfig(config) && !this.defaultApplication) {
553
599
  throw new MicrofrontendError(
554
- `Could not find default application in microfrontends configuration`,
600
+ "Could not find default application in microfrontends configuration",
555
601
  {
556
602
  type: "application",
557
603
  subtype: "not_found"
@@ -627,11 +673,11 @@ var MicrofrontendConfigIsomorphic = class {
627
673
  return app;
628
674
  }
629
675
  getApplicationByProjectId(projectId) {
630
- if (this.defaultApplication?.vercel?.projectId === projectId) {
676
+ if (this.defaultApplication?.projectId === projectId) {
631
677
  return this.defaultApplication;
632
678
  }
633
679
  return Object.values(this.childApplications).find(
634
- (app) => app.vercel?.projectId === projectId
680
+ (app) => app.projectId === projectId
635
681
  );
636
682
  }
637
683
  /**
@@ -641,7 +687,7 @@ var MicrofrontendConfigIsomorphic = class {
641
687
  getDefaultApplication() {
642
688
  if (!this.defaultApplication) {
643
689
  throw new MicrofrontendError(
644
- `Could not find default application in microfrontends configuration`,
690
+ "Could not find default application in microfrontends configuration",
645
691
  {
646
692
  type: "application",
647
693
  subtype: "not_found"
@@ -654,7 +700,7 @@ var MicrofrontendConfigIsomorphic = class {
654
700
  * Returns the configured port for the local proxy
655
701
  */
656
702
  getLocalProxyPort() {
657
- return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
703
+ return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
658
704
  }
659
705
  /**
660
706
  * Serializes the class back to the Schema type.
@@ -728,7 +774,7 @@ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
728
774
  }
729
775
  if (!defaultApplication) {
730
776
  throw new MicrofrontendError(
731
- `Could not find default application in microfrontends configuration`,
777
+ "Could not find default application in microfrontends configuration",
732
778
  {
733
779
  type: "application",
734
780
  subtype: "not_found"
@@ -1028,11 +1074,21 @@ var schema_default = {
1028
1074
  properties: {
1029
1075
  vercel: {
1030
1076
  $ref: "#/definitions/VercelOptions",
1031
- description: "Micro-Frontends wide options for Vercel."
1077
+ description: "Microfrontends wide options for Vercel.",
1078
+ deprecated: "This is being replaced by the `disableOverrides` field below."
1079
+ },
1080
+ disableOverrides: {
1081
+ type: "boolean",
1082
+ 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."
1032
1083
  },
1033
1084
  localProxy: {
1034
1085
  $ref: "#/definitions/LocalProxyOptions",
1035
- description: "Options for local proxy."
1086
+ description: "Options for local proxy.",
1087
+ deprecated: "This is being replaced by the `localProxyPort` field below."
1088
+ },
1089
+ localProxyPort: {
1090
+ type: "number",
1091
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1036
1092
  }
1037
1093
  },
1038
1094
  additionalProperties: false
@@ -1080,13 +1136,19 @@ var schema_default = {
1080
1136
  type: "object",
1081
1137
  properties: {
1082
1138
  vercel: {
1083
- $ref: "#/definitions/Vercel"
1139
+ $ref: "#/definitions/Vercel",
1140
+ deprecated: "This is being replaced by the `projectId` field below."
1141
+ },
1142
+ projectId: {
1143
+ type: "string",
1144
+ description: "Vercel project ID"
1084
1145
  },
1085
1146
  development: {
1086
1147
  $ref: "#/definitions/Development"
1087
1148
  },
1088
1149
  production: {
1089
- $ref: "#/definitions/HostConfig"
1150
+ $ref: "#/definitions/HostConfig",
1151
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1090
1152
  }
1091
1153
  },
1092
1154
  required: ["production"],
@@ -1107,11 +1169,23 @@ var schema_default = {
1107
1169
  type: "object",
1108
1170
  properties: {
1109
1171
  local: {
1110
- $ref: "#/definitions/LocalHostConfig"
1172
+ $ref: "#/definitions/LocalHostConfig",
1173
+ deprecated: "This is being replaced by the `localPort` field below."
1174
+ },
1175
+ localPort: {
1176
+ type: "number",
1177
+ 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."
1111
1178
  },
1112
1179
  fallback: {
1113
- $ref: "#/definitions/HostConfig",
1114
- 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."
1180
+ anyOf: [
1181
+ {
1182
+ $ref: "#/definitions/HostConfig"
1183
+ },
1184
+ {
1185
+ type: "string"
1186
+ }
1187
+ ],
1188
+ 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."
1115
1189
  },
1116
1190
  task: {
1117
1191
  type: "string",
@@ -1163,7 +1237,12 @@ var schema_default = {
1163
1237
  type: "object",
1164
1238
  properties: {
1165
1239
  vercel: {
1166
- $ref: "#/definitions/Vercel"
1240
+ $ref: "#/definitions/Vercel",
1241
+ deprecated: "This is being replaced by the `projectId` field below."
1242
+ },
1243
+ projectId: {
1244
+ type: "string",
1245
+ description: "Vercel project ID"
1167
1246
  },
1168
1247
  development: {
1169
1248
  $ref: "#/definitions/Development"
@@ -1173,7 +1252,8 @@ var schema_default = {
1173
1252
  description: "Groups of path expressions that are routed to this application."
1174
1253
  },
1175
1254
  production: {
1176
- $ref: "#/definitions/HostConfig"
1255
+ $ref: "#/definitions/HostConfig",
1256
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1177
1257
  }
1178
1258
  },
1179
1259
  required: ["routing"],
@@ -1469,7 +1549,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1469
1549
  const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1470
1550
  if (!defaultApplication) {
1471
1551
  throw new MicrofrontendError(
1472
- `No default application found. At least one application needs to be the default by omitting routing.`,
1552
+ "No default application found. At least one application needs to be the default by omitting routing.",
1473
1553
  { type: "config", subtype: "no_default_application" }
1474
1554
  );
1475
1555
  }