@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
@@ -268,7 +268,8 @@ var validateConfigPaths = (applicationConfigsById) => {
268
268
  if (isDefaultApp(app)) {
269
269
  continue;
270
270
  }
271
- for (const pathMatch of app.routing) {
271
+ const childApp = app;
272
+ for (const pathMatch of childApp.routing) {
272
273
  for (const path6 of pathMatch.paths) {
273
274
  const maybeError = validatePathExpression(path6);
274
275
  if (maybeError) {
@@ -288,33 +289,31 @@ var validateConfigPaths = (applicationConfigsById) => {
288
289
  }
289
290
  }
290
291
  const entries = Array.from(pathsByApplicationId.entries());
291
- entries.forEach(([path6, { applications: ids, matcher, applicationId }]) => {
292
+ for (const [path6, { applications: ids, matcher, applicationId }] of entries) {
292
293
  if (ids.length > 1) {
293
294
  errors.push(
294
295
  `Duplicate path "${path6}" for applications "${ids.join(", ")}"`
295
296
  );
296
297
  }
297
- entries.forEach(
298
- ([
299
- matchPath,
300
- { applications: matchIds, applicationId: matchApplicationId }
301
- ]) => {
302
- if (path6 === matchPath) {
303
- return;
304
- }
305
- if (applicationId === matchApplicationId) {
306
- return;
307
- }
308
- if (matcher.test(matchPath)) {
309
- const source = `"${path6}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
310
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
311
- errors.push(
312
- `Overlapping path detected between ${source} and ${destination}`
313
- );
314
- }
298
+ for (const [
299
+ matchPath,
300
+ { applications: matchIds, applicationId: matchApplicationId }
301
+ ] of entries) {
302
+ if (path6 === matchPath) {
303
+ continue;
315
304
  }
316
- );
317
- });
305
+ if (applicationId === matchApplicationId) {
306
+ continue;
307
+ }
308
+ if (matcher.test(matchPath)) {
309
+ const source = `"${path6}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
310
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
311
+ errors.push(
312
+ `Overlapping path detected between ${source} and ${destination}`
313
+ );
314
+ }
315
+ }
316
+ }
318
317
  if (errors.length) {
319
318
  throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
320
319
  type: "config",
@@ -383,7 +382,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
383
382
  const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
384
383
  if (numApplicationsWithoutRouting === 0) {
385
384
  throw new MicrofrontendError(
386
- `No default application found. At least one application needs to be the default by omitting routing.`,
385
+ "No default application found. At least one application needs to be the default by omitting routing.",
387
386
  { type: "config", subtype: "no_default_application" }
388
387
  );
389
388
  }
@@ -429,47 +428,80 @@ function generatePortFromName({
429
428
  // src/config/microfrontends-config/isomorphic/host.ts
430
429
  var Host = class {
431
430
  constructor(hostConfig, options) {
432
- const { protocol = "https", host, port } = hostConfig;
433
- this.protocol = protocol;
434
- this.host = host;
435
- this.port = Host.getPort({ port, protocol: this.protocol });
431
+ if (typeof hostConfig === "string") {
432
+ ({
433
+ protocol: this.protocol,
434
+ host: this.host,
435
+ port: this.port
436
+ } = Host.parseUrl(hostConfig));
437
+ } else {
438
+ const { protocol = "https", host, port } = hostConfig;
439
+ this.protocol = protocol;
440
+ this.host = host;
441
+ this.port = port;
442
+ }
436
443
  this.local = options?.isLocal;
437
444
  }
438
- isLocal() {
439
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
440
- }
441
- static getPort({
442
- protocol,
443
- port
444
- }) {
445
- if (!port) {
446
- if (protocol === "http") {
447
- return 80;
448
- }
449
- return 443;
445
+ static parseUrl(url) {
446
+ let hostToParse = url;
447
+ if (!/^https?:\/\//.exec(hostToParse)) {
448
+ hostToParse = `https://${hostToParse}`;
449
+ }
450
+ const parsed = new URL(hostToParse);
451
+ if (!parsed.hostname) {
452
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
453
+ }
454
+ if (parsed.hash) {
455
+ throw new Error(
456
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
457
+ );
458
+ }
459
+ if (parsed.username || parsed.password) {
460
+ throw new Error(
461
+ Host.getMicrofrontendsError(
462
+ url,
463
+ "cannot have authentication credentials (username and/or password)"
464
+ )
465
+ );
466
+ }
467
+ if (parsed.pathname !== "/") {
468
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
469
+ }
470
+ if (parsed.search) {
471
+ throw new Error(
472
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
473
+ );
450
474
  }
451
- return port;
475
+ const protocol = parsed.protocol.slice(0, -1);
476
+ return {
477
+ protocol,
478
+ host: parsed.hostname,
479
+ port: parsed.port ? Number.parseInt(parsed.port) : void 0
480
+ };
452
481
  }
453
- isDefaultPort() {
454
- return this.port === Host.getPort({ protocol: this.protocol });
482
+ static getMicrofrontendsError(url, message) {
483
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
484
+ }
485
+ isLocal() {
486
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
455
487
  }
456
- toString(opts = {}) {
457
- const url = this.toUrl(opts);
488
+ toString() {
489
+ const url = this.toUrl();
458
490
  return url.toString().replace(/\/$/, "");
459
491
  }
460
- toUrl(opts = {}) {
461
- const { includeDefaultPort } = opts;
462
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
492
+ toUrl() {
493
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
463
494
  return new URL(url);
464
495
  }
465
496
  };
466
497
  var LocalHost = class extends Host {
467
498
  constructor({
468
499
  appName,
500
+ localPort,
469
501
  ...hostConfig
470
502
  }) {
471
503
  const host = hostConfig.host ?? "localhost";
472
- const port = hostConfig.port ?? generatePortFromName({ name: appName });
504
+ const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
473
505
  const protocol = hostConfig.protocol ?? "http";
474
506
  super({ protocol, host, port });
475
507
  }
@@ -486,12 +518,17 @@ var Application = class {
486
518
  this.development = {
487
519
  local: new LocalHost({
488
520
  appName: name,
521
+ localPort: app.development?.localPort,
489
522
  ...app.development?.local
490
523
  }),
491
524
  fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
492
525
  };
493
- this.production = app.production ? new Host(app.production) : void 0;
494
- this.vercel = app.vercel;
526
+ if (app.development?.fallback) {
527
+ this.fallback = new Host(app.development.fallback);
528
+ } else if (app.production) {
529
+ this.fallback = new Host(app.production);
530
+ }
531
+ this.projectId = app.projectId ?? app.vercel?.projectId;
495
532
  this.overrides = overrides?.environment ? {
496
533
  environment: new Host(overrides.environment)
497
534
  } : void 0;
@@ -519,7 +556,16 @@ var DefaultApplication = class extends Application {
519
556
  isDefault: true
520
557
  });
521
558
  this.default = true;
522
- this.production = new Host(app.production);
559
+ const fallbackHost = app.development?.fallback ?? app.production;
560
+ if (fallbackHost === void 0) {
561
+ throw new Error(
562
+ "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
563
+ );
564
+ }
565
+ this.fallback = new Host(fallbackHost);
566
+ if (app.production) {
567
+ this.production = new Host(app.production);
568
+ }
523
569
  }
524
570
  getAssetPrefix() {
525
571
  return "";
@@ -589,7 +635,7 @@ var MicrofrontendConfigIsomorphic = class {
589
635
  }
590
636
  if (isMainConfig(config) && !this.defaultApplication) {
591
637
  throw new MicrofrontendError(
592
- `Could not find default application in microfrontends configuration`,
638
+ "Could not find default application in microfrontends configuration",
593
639
  {
594
640
  type: "application",
595
641
  subtype: "not_found"
@@ -665,11 +711,11 @@ var MicrofrontendConfigIsomorphic = class {
665
711
  return app;
666
712
  }
667
713
  getApplicationByProjectId(projectId) {
668
- if (this.defaultApplication?.vercel?.projectId === projectId) {
714
+ if (this.defaultApplication?.projectId === projectId) {
669
715
  return this.defaultApplication;
670
716
  }
671
717
  return Object.values(this.childApplications).find(
672
- (app) => app.vercel?.projectId === projectId
718
+ (app) => app.projectId === projectId
673
719
  );
674
720
  }
675
721
  /**
@@ -679,7 +725,7 @@ var MicrofrontendConfigIsomorphic = class {
679
725
  getDefaultApplication() {
680
726
  if (!this.defaultApplication) {
681
727
  throw new MicrofrontendError(
682
- `Could not find default application in microfrontends configuration`,
728
+ "Could not find default application in microfrontends configuration",
683
729
  {
684
730
  type: "application",
685
731
  subtype: "not_found"
@@ -692,7 +738,7 @@ var MicrofrontendConfigIsomorphic = class {
692
738
  * Returns the configured port for the local proxy
693
739
  */
694
740
  getLocalProxyPort() {
695
- return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
741
+ return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
696
742
  }
697
743
  /**
698
744
  * Serializes the class back to the Schema type.
@@ -766,7 +812,7 @@ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
766
812
  }
767
813
  if (!defaultApplication) {
768
814
  throw new MicrofrontendError(
769
- `Could not find default application in microfrontends configuration`,
815
+ "Could not find default application in microfrontends configuration",
770
816
  {
771
817
  type: "application",
772
818
  subtype: "not_found"
@@ -1066,11 +1112,21 @@ var schema_default = {
1066
1112
  properties: {
1067
1113
  vercel: {
1068
1114
  $ref: "#/definitions/VercelOptions",
1069
- description: "Micro-Frontends wide options for Vercel."
1115
+ description: "Microfrontends wide options for Vercel.",
1116
+ deprecated: "This is being replaced by the `disableOverrides` field below."
1117
+ },
1118
+ disableOverrides: {
1119
+ type: "boolean",
1120
+ 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."
1070
1121
  },
1071
1122
  localProxy: {
1072
1123
  $ref: "#/definitions/LocalProxyOptions",
1073
- description: "Options for local proxy."
1124
+ description: "Options for local proxy.",
1125
+ deprecated: "This is being replaced by the `localProxyPort` field below."
1126
+ },
1127
+ localProxyPort: {
1128
+ type: "number",
1129
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1074
1130
  }
1075
1131
  },
1076
1132
  additionalProperties: false
@@ -1118,13 +1174,19 @@ var schema_default = {
1118
1174
  type: "object",
1119
1175
  properties: {
1120
1176
  vercel: {
1121
- $ref: "#/definitions/Vercel"
1177
+ $ref: "#/definitions/Vercel",
1178
+ deprecated: "This is being replaced by the `projectId` field below."
1179
+ },
1180
+ projectId: {
1181
+ type: "string",
1182
+ description: "Vercel project ID"
1122
1183
  },
1123
1184
  development: {
1124
1185
  $ref: "#/definitions/Development"
1125
1186
  },
1126
1187
  production: {
1127
- $ref: "#/definitions/HostConfig"
1188
+ $ref: "#/definitions/HostConfig",
1189
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1128
1190
  }
1129
1191
  },
1130
1192
  required: ["production"],
@@ -1145,11 +1207,23 @@ var schema_default = {
1145
1207
  type: "object",
1146
1208
  properties: {
1147
1209
  local: {
1148
- $ref: "#/definitions/LocalHostConfig"
1210
+ $ref: "#/definitions/LocalHostConfig",
1211
+ deprecated: "This is being replaced by the `localPort` field below."
1212
+ },
1213
+ localPort: {
1214
+ type: "number",
1215
+ 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."
1149
1216
  },
1150
1217
  fallback: {
1151
- $ref: "#/definitions/HostConfig",
1152
- 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."
1218
+ anyOf: [
1219
+ {
1220
+ $ref: "#/definitions/HostConfig"
1221
+ },
1222
+ {
1223
+ type: "string"
1224
+ }
1225
+ ],
1226
+ 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."
1153
1227
  },
1154
1228
  task: {
1155
1229
  type: "string",
@@ -1201,7 +1275,12 @@ var schema_default = {
1201
1275
  type: "object",
1202
1276
  properties: {
1203
1277
  vercel: {
1204
- $ref: "#/definitions/Vercel"
1278
+ $ref: "#/definitions/Vercel",
1279
+ deprecated: "This is being replaced by the `projectId` field below."
1280
+ },
1281
+ projectId: {
1282
+ type: "string",
1283
+ description: "Vercel project ID"
1205
1284
  },
1206
1285
  development: {
1207
1286
  $ref: "#/definitions/Development"
@@ -1211,7 +1290,8 @@ var schema_default = {
1211
1290
  description: "Groups of path expressions that are routed to this application."
1212
1291
  },
1213
1292
  production: {
1214
- $ref: "#/definitions/HostConfig"
1293
+ $ref: "#/definitions/HostConfig",
1294
+ deprecated: "This is a duplicate of the `development.fallback` field and this will be removed soon."
1215
1295
  }
1216
1296
  },
1217
1297
  required: ["routing"],
@@ -1507,7 +1587,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1507
1587
  const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1508
1588
  if (!defaultApplication) {
1509
1589
  throw new MicrofrontendError(
1510
- `No default application found. At least one application needs to be the default by omitting routing.`,
1590
+ "No default application found. At least one application needs to be the default by omitting routing.",
1511
1591
  { type: "config", subtype: "no_default_application" }
1512
1592
  );
1513
1593
  }
@@ -1560,7 +1640,7 @@ function loadConfig({
1560
1640
  return void 0;
1561
1641
  }
1562
1642
  const app = config.config.getApplication(appName);
1563
- const port = app.development.local.port;
1643
+ const port = app.development.local.port ?? (app.development.local.protocol === "https" ? 443 : 80);
1564
1644
  return { port };
1565
1645
  }
1566
1646
  // Annotate the CommonJS export names for ESM import in node: