@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
@@ -264,7 +264,8 @@ var validateConfigPaths = (applicationConfigsById) => {
264
264
  if (isDefaultApp(app)) {
265
265
  continue;
266
266
  }
267
- for (const pathMatch of app.routing) {
267
+ const childApp = app;
268
+ for (const pathMatch of childApp.routing) {
268
269
  for (const path5 of pathMatch.paths) {
269
270
  const maybeError = validatePathExpression(path5);
270
271
  if (maybeError) {
@@ -284,33 +285,31 @@ var validateConfigPaths = (applicationConfigsById) => {
284
285
  }
285
286
  }
286
287
  const entries = Array.from(pathsByApplicationId.entries());
287
- entries.forEach(([path5, { applications: ids, matcher, applicationId }]) => {
288
+ for (const [path5, { applications: ids, matcher, applicationId }] of entries) {
288
289
  if (ids.length > 1) {
289
290
  errors.push(
290
291
  `Duplicate path "${path5}" for applications "${ids.join(", ")}"`
291
292
  );
292
293
  }
293
- entries.forEach(
294
- ([
295
- matchPath,
296
- { applications: matchIds, applicationId: matchApplicationId }
297
- ]) => {
298
- if (path5 === matchPath) {
299
- return;
300
- }
301
- if (applicationId === matchApplicationId) {
302
- return;
303
- }
304
- if (matcher.test(matchPath)) {
305
- const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
306
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
307
- errors.push(
308
- `Overlapping path detected between ${source} and ${destination}`
309
- );
310
- }
294
+ for (const [
295
+ matchPath,
296
+ { applications: matchIds, applicationId: matchApplicationId }
297
+ ] of entries) {
298
+ if (path5 === matchPath) {
299
+ continue;
311
300
  }
312
- );
313
- });
301
+ if (applicationId === matchApplicationId) {
302
+ continue;
303
+ }
304
+ if (matcher.test(matchPath)) {
305
+ const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
306
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
307
+ errors.push(
308
+ `Overlapping path detected between ${source} and ${destination}`
309
+ );
310
+ }
311
+ }
312
+ }
314
313
  if (errors.length) {
315
314
  throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
316
315
  type: "config",
@@ -379,7 +378,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
379
378
  const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
380
379
  if (numApplicationsWithoutRouting === 0) {
381
380
  throw new MicrofrontendError(
382
- `No default application found. At least one application needs to be the default by omitting routing.`,
381
+ "No default application found. At least one application needs to be the default by omitting routing.",
383
382
  { type: "config", subtype: "no_default_application" }
384
383
  );
385
384
  }
@@ -425,47 +424,80 @@ function generatePortFromName({
425
424
  // src/config/microfrontends-config/isomorphic/host.ts
426
425
  var Host = class {
427
426
  constructor(hostConfig, options) {
428
- const { protocol = "https", host, port } = hostConfig;
429
- this.protocol = protocol;
430
- this.host = host;
431
- this.port = Host.getPort({ port, protocol: this.protocol });
427
+ if (typeof hostConfig === "string") {
428
+ ({
429
+ protocol: this.protocol,
430
+ host: this.host,
431
+ port: this.port
432
+ } = Host.parseUrl(hostConfig));
433
+ } else {
434
+ const { protocol = "https", host, port } = hostConfig;
435
+ this.protocol = protocol;
436
+ this.host = host;
437
+ this.port = port;
438
+ }
432
439
  this.local = options?.isLocal;
433
440
  }
434
- isLocal() {
435
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
436
- }
437
- static getPort({
438
- protocol,
439
- port
440
- }) {
441
- if (!port) {
442
- if (protocol === "http") {
443
- return 80;
444
- }
445
- return 443;
441
+ static parseUrl(url) {
442
+ let hostToParse = url;
443
+ if (!/^https?:\/\//.exec(hostToParse)) {
444
+ hostToParse = `https://${hostToParse}`;
445
+ }
446
+ const parsed = new URL(hostToParse);
447
+ if (!parsed.hostname) {
448
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
446
449
  }
447
- return port;
450
+ if (parsed.hash) {
451
+ throw new Error(
452
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
453
+ );
454
+ }
455
+ if (parsed.username || parsed.password) {
456
+ throw new Error(
457
+ Host.getMicrofrontendsError(
458
+ url,
459
+ "cannot have authentication credentials (username and/or password)"
460
+ )
461
+ );
462
+ }
463
+ if (parsed.pathname !== "/") {
464
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
465
+ }
466
+ if (parsed.search) {
467
+ throw new Error(
468
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
469
+ );
470
+ }
471
+ const protocol = parsed.protocol.slice(0, -1);
472
+ return {
473
+ protocol,
474
+ host: parsed.hostname,
475
+ port: parsed.port ? Number.parseInt(parsed.port) : void 0
476
+ };
477
+ }
478
+ static getMicrofrontendsError(url, message) {
479
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
448
480
  }
449
- isDefaultPort() {
450
- return this.port === Host.getPort({ protocol: this.protocol });
481
+ isLocal() {
482
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
451
483
  }
452
- toString(opts = {}) {
453
- const url = this.toUrl(opts);
484
+ toString() {
485
+ const url = this.toUrl();
454
486
  return url.toString().replace(/\/$/, "");
455
487
  }
456
- toUrl(opts = {}) {
457
- const { includeDefaultPort } = opts;
458
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
488
+ toUrl() {
489
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
459
490
  return new URL(url);
460
491
  }
461
492
  };
462
493
  var LocalHost = class extends Host {
463
494
  constructor({
464
495
  appName,
496
+ localPort,
465
497
  ...hostConfig
466
498
  }) {
467
499
  const host = hostConfig.host ?? "localhost";
468
- const port = hostConfig.port ?? generatePortFromName({ name: appName });
500
+ const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
469
501
  const protocol = hostConfig.protocol ?? "http";
470
502
  super({ protocol, host, port });
471
503
  }
@@ -482,12 +514,17 @@ var Application = class {
482
514
  this.development = {
483
515
  local: new LocalHost({
484
516
  appName: name,
517
+ localPort: app.development?.localPort,
485
518
  ...app.development?.local
486
519
  }),
487
520
  fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
488
521
  };
489
- this.production = app.production ? new Host(app.production) : void 0;
490
- this.vercel = app.vercel;
522
+ if (app.development?.fallback) {
523
+ this.fallback = new Host(app.development.fallback);
524
+ } else if (app.production) {
525
+ this.fallback = new Host(app.production);
526
+ }
527
+ this.projectId = app.projectId ?? app.vercel?.projectId;
491
528
  this.overrides = overrides?.environment ? {
492
529
  environment: new Host(overrides.environment)
493
530
  } : void 0;
@@ -515,7 +552,16 @@ var DefaultApplication = class extends Application {
515
552
  isDefault: true
516
553
  });
517
554
  this.default = true;
518
- this.production = new Host(app.production);
555
+ const fallbackHost = app.development?.fallback ?? app.production;
556
+ if (fallbackHost === void 0) {
557
+ throw new Error(
558
+ "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
559
+ );
560
+ }
561
+ this.fallback = new Host(fallbackHost);
562
+ if (app.production) {
563
+ this.production = new Host(app.production);
564
+ }
519
565
  }
520
566
  getAssetPrefix() {
521
567
  return "";
@@ -585,7 +631,7 @@ var MicrofrontendConfigIsomorphic = class {
585
631
  }
586
632
  if (isMainConfig(config) && !this.defaultApplication) {
587
633
  throw new MicrofrontendError(
588
- `Could not find default application in microfrontends configuration`,
634
+ "Could not find default application in microfrontends configuration",
589
635
  {
590
636
  type: "application",
591
637
  subtype: "not_found"
@@ -661,11 +707,11 @@ var MicrofrontendConfigIsomorphic = class {
661
707
  return app;
662
708
  }
663
709
  getApplicationByProjectId(projectId) {
664
- if (this.defaultApplication?.vercel?.projectId === projectId) {
710
+ if (this.defaultApplication?.projectId === projectId) {
665
711
  return this.defaultApplication;
666
712
  }
667
713
  return Object.values(this.childApplications).find(
668
- (app) => app.vercel?.projectId === projectId
714
+ (app) => app.projectId === projectId
669
715
  );
670
716
  }
671
717
  /**
@@ -675,7 +721,7 @@ var MicrofrontendConfigIsomorphic = class {
675
721
  getDefaultApplication() {
676
722
  if (!this.defaultApplication) {
677
723
  throw new MicrofrontendError(
678
- `Could not find default application in microfrontends configuration`,
724
+ "Could not find default application in microfrontends configuration",
679
725
  {
680
726
  type: "application",
681
727
  subtype: "not_found"
@@ -688,7 +734,7 @@ var MicrofrontendConfigIsomorphic = class {
688
734
  * Returns the configured port for the local proxy
689
735
  */
690
736
  getLocalProxyPort() {
691
- return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
737
+ return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
692
738
  }
693
739
  /**
694
740
  * Serializes the class back to the Schema type.
@@ -762,7 +808,7 @@ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
762
808
  }
763
809
  if (!defaultApplication) {
764
810
  throw new MicrofrontendError(
765
- `Could not find default application in microfrontends configuration`,
811
+ "Could not find default application in microfrontends configuration",
766
812
  {
767
813
  type: "application",
768
814
  subtype: "not_found"
@@ -1062,11 +1108,21 @@ var schema_default = {
1062
1108
  properties: {
1063
1109
  vercel: {
1064
1110
  $ref: "#/definitions/VercelOptions",
1065
- description: "Micro-Frontends wide options for Vercel."
1111
+ description: "Microfrontends wide options for Vercel.",
1112
+ deprecated: "This is being replaced by the `disableOverrides` field below."
1113
+ },
1114
+ disableOverrides: {
1115
+ type: "boolean",
1116
+ 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."
1066
1117
  },
1067
1118
  localProxy: {
1068
1119
  $ref: "#/definitions/LocalProxyOptions",
1069
- description: "Options for local proxy."
1120
+ description: "Options for local proxy.",
1121
+ deprecated: "This is being replaced by the `localProxyPort` field below."
1122
+ },
1123
+ localProxyPort: {
1124
+ type: "number",
1125
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1070
1126
  }
1071
1127
  },
1072
1128
  additionalProperties: false
@@ -1114,13 +1170,19 @@ var schema_default = {
1114
1170
  type: "object",
1115
1171
  properties: {
1116
1172
  vercel: {
1117
- $ref: "#/definitions/Vercel"
1173
+ $ref: "#/definitions/Vercel",
1174
+ deprecated: "This is being replaced by the `projectId` field below."
1175
+ },
1176
+ projectId: {
1177
+ type: "string",
1178
+ description: "Vercel project ID"
1118
1179
  },
1119
1180
  development: {
1120
1181
  $ref: "#/definitions/Development"
1121
1182
  },
1122
1183
  production: {
1123
- $ref: "#/definitions/HostConfig"
1184
+ $ref: "#/definitions/HostConfig",
1185
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1124
1186
  }
1125
1187
  },
1126
1188
  required: ["production"],
@@ -1141,11 +1203,23 @@ var schema_default = {
1141
1203
  type: "object",
1142
1204
  properties: {
1143
1205
  local: {
1144
- $ref: "#/definitions/LocalHostConfig"
1206
+ $ref: "#/definitions/LocalHostConfig",
1207
+ deprecated: "This is being replaced by the `localPort` field below."
1208
+ },
1209
+ localPort: {
1210
+ type: "number",
1211
+ 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."
1145
1212
  },
1146
1213
  fallback: {
1147
- $ref: "#/definitions/HostConfig",
1148
- 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."
1214
+ anyOf: [
1215
+ {
1216
+ $ref: "#/definitions/HostConfig"
1217
+ },
1218
+ {
1219
+ type: "string"
1220
+ }
1221
+ ],
1222
+ 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."
1149
1223
  },
1150
1224
  task: {
1151
1225
  type: "string",
@@ -1197,7 +1271,12 @@ var schema_default = {
1197
1271
  type: "object",
1198
1272
  properties: {
1199
1273
  vercel: {
1200
- $ref: "#/definitions/Vercel"
1274
+ $ref: "#/definitions/Vercel",
1275
+ deprecated: "This is being replaced by the `projectId` field below."
1276
+ },
1277
+ projectId: {
1278
+ type: "string",
1279
+ description: "Vercel project ID"
1201
1280
  },
1202
1281
  development: {
1203
1282
  $ref: "#/definitions/Development"
@@ -1207,7 +1286,8 @@ var schema_default = {
1207
1286
  description: "Groups of path expressions that are routed to this application."
1208
1287
  },
1209
1288
  production: {
1210
- $ref: "#/definitions/HostConfig"
1289
+ $ref: "#/definitions/HostConfig",
1290
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1211
1291
  }
1212
1292
  },
1213
1293
  required: ["routing"],
@@ -1503,7 +1583,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1503
1583
  const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1504
1584
  if (!defaultApplication) {
1505
1585
  throw new MicrofrontendError(
1506
- `No default application found. At least one application needs to be the default by omitting routing.`,
1586
+ "No default application found. At least one application needs to be the default by omitting routing.",
1507
1587
  { type: "config", subtype: "no_default_application" }
1508
1588
  );
1509
1589
  }