@vercel/microfrontends 2.0.0-canary.1 → 2.0.0-canary.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 (45) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +21 -4
  3. package/dist/bin/cli.cjs +142 -13
  4. package/dist/config.cjs +123 -3
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +33 -2
  7. package/dist/config.js +122 -2
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +128 -4
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +127 -3
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +128 -4
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +127 -3
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +128 -4
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +4 -3
  20. package/dist/microfrontends/server.js +127 -3
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/next/config.cjs +131 -131
  23. package/dist/next/config.cjs.map +1 -1
  24. package/dist/next/config.d.ts +5 -0
  25. package/dist/next/config.js +130 -130
  26. package/dist/next/config.js.map +1 -1
  27. package/dist/next/middleware.cjs +125 -105
  28. package/dist/next/middleware.cjs.map +1 -1
  29. package/dist/next/middleware.js +125 -105
  30. package/dist/next/middleware.js.map +1 -1
  31. package/dist/next/testing.cjs +126 -6
  32. package/dist/next/testing.cjs.map +1 -1
  33. package/dist/next/testing.d.ts +2 -2
  34. package/dist/next/testing.js +124 -4
  35. package/dist/next/testing.js.map +1 -1
  36. package/dist/overrides.d.ts +3 -3
  37. package/dist/schema.d.ts +2 -2
  38. package/dist/{types-4299bff1.d.ts → types-88602303.d.ts} +1 -1
  39. package/dist/{types-0deb756b.d.ts → types-e7523e61.d.ts} +1 -1
  40. package/dist/utils/mfe-port.cjs +128 -4
  41. package/dist/utils/mfe-port.cjs.map +1 -1
  42. package/dist/utils/mfe-port.js +127 -3
  43. package/dist/utils/mfe-port.js.map +1 -1
  44. package/dist/validation.d.ts +1 -1
  45. package/package.json +7 -2
@@ -5,6 +5,11 @@ type TransformKeys = 'assetPrefix' | 'buildId' | 'draftMode' | 'redirects' | 're
5
5
  interface WithMicrofrontendsOptions {
6
6
  /**
7
7
  * Explicitly set the name of the application instead of using the name from the package.json.
8
+ * This option is useful for cases where you are trying to import next.config.js in a different
9
+ * package, such as for the Storybook Next.js integration.
10
+ *
11
+ * NOTE: This option should not be used in most cases. Issues on Vercel may occur
12
+ * if it differs from the project name.
8
13
  */
9
14
  appName?: string;
10
15
  isProduction?: () => boolean;
@@ -368,8 +368,106 @@ function findConfig({ dir }) {
368
368
  // src/config/microfrontends-config/isomorphic/index.ts
369
369
  import { parse as parse2 } from "jsonc-parser";
370
370
 
371
+ // src/config/microfrontends-config/client/index.ts
372
+ import { pathToRegexp } from "path-to-regexp";
373
+ var regexpCache = /* @__PURE__ */ new Map();
374
+ var getRegexp = (path6) => {
375
+ const existing = regexpCache.get(path6);
376
+ if (existing) {
377
+ return existing;
378
+ }
379
+ const regexp = pathToRegexp(path6);
380
+ regexpCache.set(path6, regexp);
381
+ return regexp;
382
+ };
383
+ var MicrofrontendConfigClient = class {
384
+ constructor(config, opts) {
385
+ this.pathCache = {};
386
+ this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;
387
+ for (const app of Object.values(config.applications)) {
388
+ if (app.routing) {
389
+ if (app.routing.some((match) => match.flag)) {
390
+ this.hasFlaggedPaths = true;
391
+ }
392
+ const newRouting = [];
393
+ const pathsWithoutFlags = [];
394
+ for (const group of app.routing) {
395
+ if (group.flag) {
396
+ if (opts?.removeFlaggedPaths) {
397
+ continue;
398
+ }
399
+ if (group.group) {
400
+ delete group.group;
401
+ }
402
+ newRouting.push(group);
403
+ } else {
404
+ pathsWithoutFlags.push(...group.paths);
405
+ }
406
+ }
407
+ if (pathsWithoutFlags.length > 0) {
408
+ newRouting.push({ paths: pathsWithoutFlags });
409
+ }
410
+ app.routing = newRouting;
411
+ }
412
+ }
413
+ this.serialized = config;
414
+ if (this.hasFlaggedPaths) {
415
+ this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;
416
+ }
417
+ this.applications = config.applications;
418
+ }
419
+ /**
420
+ * Create a new `MicrofrontendConfigClient` from a JSON string.
421
+ * Config must be passed in to remain framework agnostic
422
+ */
423
+ static fromEnv(config) {
424
+ if (!config) {
425
+ throw new Error(
426
+ "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
427
+ );
428
+ }
429
+ return new MicrofrontendConfigClient(JSON.parse(config));
430
+ }
431
+ isEqual(other) {
432
+ return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
433
+ }
434
+ getApplicationNameForPath(path6) {
435
+ if (!path6.startsWith("/")) {
436
+ throw new Error(`Path must start with a /`);
437
+ }
438
+ if (this.pathCache[path6]) {
439
+ return this.pathCache[path6];
440
+ }
441
+ const pathname = new URL(path6, "https://example.com").pathname;
442
+ for (const [name, application] of Object.entries(this.applications)) {
443
+ if (application.routing) {
444
+ for (const group of application.routing) {
445
+ for (const childPath of group.paths) {
446
+ const regexp = getRegexp(childPath);
447
+ if (regexp.test(pathname)) {
448
+ this.pathCache[path6] = name;
449
+ return name;
450
+ }
451
+ }
452
+ }
453
+ }
454
+ }
455
+ const defaultApplication = Object.entries(this.applications).find(
456
+ ([, application]) => application.default
457
+ );
458
+ if (!defaultApplication) {
459
+ return null;
460
+ }
461
+ this.pathCache[path6] = defaultApplication[0];
462
+ return defaultApplication[0];
463
+ }
464
+ serialize() {
465
+ return this.serialized;
466
+ }
467
+ };
468
+
371
469
  // src/config/microfrontends-config/isomorphic/validation.ts
372
- import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
470
+ import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
373
471
  var LIST_FORMATTER = new Intl.ListFormat("en", {
374
472
  style: "long",
375
473
  type: "conjunction"
@@ -397,7 +495,7 @@ var validateConfigPaths = (applicationConfigsById) => {
397
495
  } else {
398
496
  pathsByApplicationId.set(path6, {
399
497
  applications: [id],
400
- matcher: pathToRegexp(path6),
498
+ matcher: pathToRegexp2(path6),
401
499
  applicationId: id
402
500
  });
403
501
  }
@@ -902,6 +1000,28 @@ var MicrofrontendConfigIsomorphic = class {
902
1000
  getLocalProxyPort() {
903
1001
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
904
1002
  }
1003
+ toClientConfig(options) {
1004
+ const applications = Object.fromEntries(
1005
+ Object.entries(this.childApplications).map(([name, application]) => [
1006
+ hashApplicationName(name),
1007
+ {
1008
+ default: false,
1009
+ routing: application.routing
1010
+ }
1011
+ ])
1012
+ );
1013
+ applications[hashApplicationName(this.defaultApplication.name)] = {
1014
+ default: true
1015
+ };
1016
+ return new MicrofrontendConfigClient(
1017
+ {
1018
+ applications
1019
+ },
1020
+ {
1021
+ removeFlaggedPaths: options?.removeFlaggedPaths
1022
+ }
1023
+ );
1024
+ }
905
1025
  /**
906
1026
  * Serializes the class back to the Schema type.
907
1027
  *
@@ -1321,6 +1441,7 @@ var MicrofrontendsServer = class {
1321
1441
  * This can return either a Child or Main configuration.
1322
1442
  */
1323
1443
  static infer({
1444
+ appName,
1324
1445
  directory,
1325
1446
  filePath,
1326
1447
  cookies
@@ -1333,7 +1454,10 @@ var MicrofrontendsServer = class {
1333
1454
  }
1334
1455
  try {
1335
1456
  const packageRoot = findPackageRoot(directory);
1336
- const applicationContext = getApplicationContext({ packageRoot });
1457
+ const applicationContext = getApplicationContext({
1458
+ appName,
1459
+ packageRoot
1460
+ });
1337
1461
  const maybeConfig = findConfig({ dir: packageRoot });
1338
1462
  if (maybeConfig) {
1339
1463
  return MicrofrontendsServer.fromFile({
@@ -1766,128 +1890,6 @@ var transforms = {
1766
1890
  webpack: transform7
1767
1891
  };
1768
1892
 
1769
- // src/config/microfrontends-config/client/index.ts
1770
- import { pathToRegexp as pathToRegexp2 } from "path-to-regexp";
1771
- var regexpCache = /* @__PURE__ */ new Map();
1772
- var getRegexp = (path6) => {
1773
- const existing = regexpCache.get(path6);
1774
- if (existing) {
1775
- return existing;
1776
- }
1777
- const regexp = pathToRegexp2(path6);
1778
- regexpCache.set(path6, regexp);
1779
- return regexp;
1780
- };
1781
- var MicrofrontendConfigClient = class {
1782
- constructor(config, opts) {
1783
- this.pathCache = {};
1784
- this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;
1785
- for (const app of Object.values(config.applications)) {
1786
- if (app.routing) {
1787
- if (app.routing.some((match) => match.flag)) {
1788
- this.hasFlaggedPaths = true;
1789
- }
1790
- const newRouting = [];
1791
- const pathsWithoutFlags = [];
1792
- for (const group of app.routing) {
1793
- if (group.flag) {
1794
- if (opts?.removeFlaggedPaths) {
1795
- continue;
1796
- }
1797
- if (group.group) {
1798
- delete group.group;
1799
- }
1800
- newRouting.push(group);
1801
- } else {
1802
- pathsWithoutFlags.push(...group.paths);
1803
- }
1804
- }
1805
- if (pathsWithoutFlags.length > 0) {
1806
- newRouting.push({ paths: pathsWithoutFlags });
1807
- }
1808
- app.routing = newRouting;
1809
- }
1810
- }
1811
- this.serialized = config;
1812
- if (this.hasFlaggedPaths) {
1813
- this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;
1814
- }
1815
- this.applications = config.applications;
1816
- }
1817
- /**
1818
- * Create a new `MicrofrontendConfigClient` from a JSON string.
1819
- * Config must be passed in to remain framework agnostic
1820
- */
1821
- static fromEnv(config) {
1822
- if (!config) {
1823
- throw new Error(
1824
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
1825
- );
1826
- }
1827
- return new MicrofrontendConfigClient(JSON.parse(config));
1828
- }
1829
- isEqual(other) {
1830
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
1831
- }
1832
- getApplicationNameForPath(path6) {
1833
- if (!path6.startsWith("/")) {
1834
- throw new Error(`Path must start with a /`);
1835
- }
1836
- if (this.pathCache[path6]) {
1837
- return this.pathCache[path6];
1838
- }
1839
- const pathname = new URL(path6, "https://example.com").pathname;
1840
- for (const [name, application] of Object.entries(this.applications)) {
1841
- if (application.routing) {
1842
- for (const group of application.routing) {
1843
- for (const childPath of group.paths) {
1844
- const regexp = getRegexp(childPath);
1845
- if (regexp.test(pathname)) {
1846
- this.pathCache[path6] = name;
1847
- return name;
1848
- }
1849
- }
1850
- }
1851
- }
1852
- }
1853
- const defaultApplication = Object.entries(this.applications).find(
1854
- ([, application]) => application.default
1855
- );
1856
- if (!defaultApplication) {
1857
- return null;
1858
- }
1859
- this.pathCache[path6] = defaultApplication[0];
1860
- return defaultApplication[0];
1861
- }
1862
- serialize() {
1863
- return this.serialized;
1864
- }
1865
- };
1866
-
1867
- // src/config/microfrontends-config/client/from-isomorphic-config.ts
1868
- function fromIsomorphicConfig(config, options) {
1869
- const applications = Object.fromEntries(
1870
- Object.entries(config.childApplications).map(([name, application]) => [
1871
- hashApplicationName(name),
1872
- {
1873
- default: false,
1874
- routing: application.routing
1875
- }
1876
- ])
1877
- );
1878
- applications[hashApplicationName(config.defaultApplication.name)] = {
1879
- default: true
1880
- };
1881
- return new MicrofrontendConfigClient(
1882
- {
1883
- applications
1884
- },
1885
- {
1886
- removeFlaggedPaths: options?.removeFlaggedPaths
1887
- }
1888
- );
1889
- }
1890
-
1891
1893
  // src/next/config/env.ts
1892
1894
  function debugEnv(env) {
1893
1895
  if (process.env.MFE_DEBUG) {
@@ -1913,7 +1915,7 @@ function setEnvironment({
1913
1915
  NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1914
1916
  NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
1915
1917
  NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
1916
- fromIsomorphicConfig(microfrontends.config, {
1918
+ microfrontends.config.toClientConfig({
1917
1919
  removeFlaggedPaths: true
1918
1920
  }).serialize()
1919
1921
  ),
@@ -1923,10 +1925,7 @@ function setEnvironment({
1923
1925
  };
1924
1926
  const serverEnvs = {
1925
1927
  MFE_CURRENT_APPLICATION: app.name,
1926
- MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig()),
1927
- MFE_CLIENT_CONFIG_FULL: JSON.stringify(
1928
- fromIsomorphicConfig(microfrontends.config).serialize()
1929
- )
1928
+ MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1930
1929
  };
1931
1930
  const allEnvs = { ...clientEnvs, ...serverEnvs };
1932
1931
  for (const [key, value] of Object.entries(allEnvs)) {
@@ -1951,6 +1950,7 @@ function withMicrofrontends(nextConfig, opts) {
1951
1950
  }
1952
1951
  const { name: fromApp } = getApplicationContext(opts);
1953
1952
  const microfrontends = MicrofrontendsServer.infer({
1953
+ appName: fromApp,
1954
1954
  filePath: opts?.configPath
1955
1955
  });
1956
1956
  const app = microfrontends.config.getApplication(fromApp);