@vercel/microfrontends 1.0.1-canary.0 → 1.0.1-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 (44) hide show
  1. package/dist/bin/cli.cjs +143 -44
  2. package/dist/config.cjs +39 -26
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.js +39 -26
  5. package/dist/config.js.map +1 -1
  6. package/dist/{sveltekit.cjs → experimental/sveltekit.cjs} +39 -26
  7. package/dist/experimental/sveltekit.cjs.map +1 -0
  8. package/dist/{sveltekit.js → experimental/sveltekit.js} +39 -26
  9. package/dist/experimental/sveltekit.js.map +1 -0
  10. package/dist/experimental/vite.cjs +1758 -0
  11. package/dist/experimental/vite.cjs.map +1 -0
  12. package/dist/experimental/vite.d.ts +29 -0
  13. package/dist/experimental/vite.js +1723 -0
  14. package/dist/experimental/vite.js.map +1 -0
  15. package/dist/microfrontends/server.cjs +39 -26
  16. package/dist/microfrontends/server.cjs.map +1 -1
  17. package/dist/microfrontends/server.js +39 -26
  18. package/dist/microfrontends/server.js.map +1 -1
  19. package/dist/microfrontends.cjs +39 -26
  20. package/dist/microfrontends.cjs.map +1 -1
  21. package/dist/microfrontends.js +39 -26
  22. package/dist/microfrontends.js.map +1 -1
  23. package/dist/next/config.cjs +220 -135
  24. package/dist/next/config.cjs.map +1 -1
  25. package/dist/next/config.d.ts +7 -1
  26. package/dist/next/config.js +220 -135
  27. package/dist/next/config.js.map +1 -1
  28. package/dist/next/middleware.cjs +69 -31
  29. package/dist/next/middleware.cjs.map +1 -1
  30. package/dist/next/middleware.js +69 -31
  31. package/dist/next/middleware.js.map +1 -1
  32. package/dist/next/testing.cjs +39 -26
  33. package/dist/next/testing.cjs.map +1 -1
  34. package/dist/next/testing.d.ts +1 -1
  35. package/dist/next/testing.js +40 -27
  36. package/dist/next/testing.js.map +1 -1
  37. package/dist/utils/mfe-port.cjs +39 -26
  38. package/dist/utils/mfe-port.cjs.map +1 -1
  39. package/dist/utils/mfe-port.js +39 -26
  40. package/dist/utils/mfe-port.js.map +1 -1
  41. package/package.json +22 -9
  42. package/dist/sveltekit.cjs.map +0 -1
  43. package/dist/sveltekit.js.map +0 -1
  44. /package/dist/{sveltekit.d.ts → experimental/sveltekit.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { NextConfig } from 'next';
2
2
 
3
- type TransformKeys = 'assetPrefix' | 'draftMode' | 'redirects' | 'rewrites' | 'serverActions' | 'webpack';
3
+ type TransformKeys = 'assetPrefix' | 'buildId' | 'draftMode' | 'redirects' | 'rewrites' | 'serverActions' | 'webpack';
4
4
 
5
5
  interface WithMicrofrontendsOptions {
6
6
  /**
@@ -10,6 +10,12 @@ interface WithMicrofrontendsOptions {
10
10
  isProduction?: () => boolean;
11
11
  debug?: boolean;
12
12
  skipTransforms?: TransformKeys[];
13
+ /**
14
+ * True to enable support for Next.js pages router. This is disabled by
15
+ * default since it modifies Webpack chunking behavior, so it should only be
16
+ * enabled when necessary.
17
+ */
18
+ supportPagesRouter?: boolean;
13
19
  configPath?: string;
14
20
  }
15
21
 
@@ -245,16 +245,17 @@ var validateConfigPaths = (applicationConfigsById) => {
245
245
  const maybeError = validatePathExpression(path5);
246
246
  if (maybeError) {
247
247
  errors.push(maybeError);
248
- }
249
- const existing = pathsByApplicationId.get(path5);
250
- if (existing) {
251
- existing.applications.push(id);
252
248
  } else {
253
- pathsByApplicationId.set(path5, {
254
- applications: [id],
255
- matcher: pathToRegexp2(path5),
256
- applicationId: id
257
- });
249
+ const existing = pathsByApplicationId.get(path5);
250
+ if (existing) {
251
+ existing.applications.push(id);
252
+ } else {
253
+ pathsByApplicationId.set(path5, {
254
+ applications: [id],
255
+ matcher: pathToRegexp2(path5),
256
+ applicationId: id
257
+ });
258
+ }
258
259
  }
259
260
  }
260
261
  }
@@ -294,26 +295,38 @@ var validateConfigPaths = (applicationConfigsById) => {
294
295
  };
295
296
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
296
297
  function validatePathExpression(path5) {
297
- const tokens = parsePathRegexp(path5);
298
- for (let i = 0; i < tokens.length; i++) {
299
- const token = tokens[i];
300
- if (token === void 0) {
301
- return `token ${i} in ${path5} is undefined, this shouldn't happen`;
302
- }
303
- if (typeof token !== "string") {
304
- if (token.pattern !== PATH_DEFAULT_PATTERN) {
305
- return `Path ${path5} cannot use a regular expression wildcard`;
306
- }
307
- if (token.prefix !== "/") {
308
- return `Wildcard :${token.name} must be immediately after a / in ${path5}`;
309
- }
310
- if (token.suffix) {
311
- return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
298
+ try {
299
+ const tokens = parsePathRegexp(path5);
300
+ if (/(?<!\\)\{/.test(path5)) {
301
+ return `Optional paths are not supported: ${path5}`;
302
+ }
303
+ if (/(?<!\\|\()\?/.test(path5)) {
304
+ return `Optional paths are not supported: ${path5}`;
305
+ }
306
+ if (/\/[^/]*(?<!\\):[^/]*(?<!\\):[^/]*/.test(path5)) {
307
+ return `Only one wildcard is allowed per path segment: ${path5}`;
308
+ }
309
+ for (let i = 0; i < tokens.length; i++) {
310
+ const token = tokens[i];
311
+ if (token === void 0) {
312
+ return `token ${i} in ${path5} is undefined, this shouldn't happen`;
312
313
  }
313
- if (token.modifier && i !== tokens.length - 1) {
314
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path5}. Modifiers are only allowed in the last path component`;
314
+ if (typeof token !== "string") {
315
+ if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
316
+ // Only limited regex is supported for now, due to performance considerations
317
+ !/^(?<allowed>[\w]+(?:\|[^|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^|()]+)+)\)\.\*$/.test(
318
+ token.pattern
319
+ )) {
320
+ return `Path ${path5} cannot use unsupported regular expression wildcard`;
321
+ }
322
+ if (token.modifier && i !== tokens.length - 1) {
323
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path5}. Modifiers are only allowed in the last path component`;
324
+ }
315
325
  }
316
326
  }
327
+ } catch (e) {
328
+ const message = e instanceof Error ? e.message : String(e);
329
+ return `Path ${path5} could not be parsed into regexp: ${message}`;
317
330
  }
318
331
  return void 0;
319
332
  }
@@ -1687,8 +1700,30 @@ function transform(args) {
1687
1700
  };
1688
1701
  }
1689
1702
 
1690
- // src/next/config/transforms/draft-mode.ts
1703
+ // src/next/config/transforms/build-id.ts
1704
+ import { nanoid } from "nanoid";
1691
1705
  function transform2(args) {
1706
+ const { app, next, opts } = args;
1707
+ if (!opts?.supportPagesRouter) {
1708
+ return { next };
1709
+ }
1710
+ if (next.generateBuildId !== void 0) {
1711
+ throw new Error(
1712
+ '"generateBuildId" must not already be set in next.config.js when using microfrontends with "supportPagesRouter"'
1713
+ );
1714
+ }
1715
+ if (!app.isDefault()) {
1716
+ next.generateBuildId = () => {
1717
+ return `${app.getAssetPrefix()}-${nanoid()}`;
1718
+ };
1719
+ }
1720
+ return {
1721
+ next
1722
+ };
1723
+ }
1724
+
1725
+ // src/next/config/transforms/draft-mode.ts
1726
+ function transform3(args) {
1692
1727
  const { next } = args;
1693
1728
  if (next.experimental?.multiZoneDraftMode !== void 0) {
1694
1729
  return {
@@ -1702,35 +1737,125 @@ function transform2(args) {
1702
1737
  };
1703
1738
  }
1704
1739
 
1740
+ // src/next/utils/route-to-local-proxy.ts
1741
+ function routeToLocalProxy() {
1742
+ const isDevEnv = (process.env.VERCEL_ENV ?? "development") === "development";
1743
+ return isDevEnv && Boolean(process.env.TURBO_TASK_HAS_MFE_PROXY);
1744
+ }
1745
+
1705
1746
  // src/next/config/transforms/redirects.ts
1706
- function transform3(args) {
1747
+ function transform4(args) {
1707
1748
  const { next, microfrontend, opts } = args;
1708
1749
  const isProduction2 = opts?.isProduction ?? false;
1709
- const isDevEnv = (process.env.VERCEL_ENV ?? "development") === "development";
1710
- const requireLocalProxyHeader = !isProduction2 && isDevEnv && Boolean(process.env.TURBO_TASK_HAS_MFE_PROXY) && !process.env.MFE_DISABLE_LOCAL_PROXY_REWRITE;
1750
+ const requireLocalProxyHeader = routeToLocalProxy() && !isProduction2 && !process.env.MFE_DISABLE_LOCAL_PROXY_REWRITE;
1711
1751
  if (requireLocalProxyHeader) {
1712
- const proxyRedirect = {
1713
- source: "/:path*",
1714
- destination: `http://localhost:${microfrontend.getLocalProxyPort()}/:path*`,
1715
- permanent: false,
1716
- missing: [{ type: "header", key: "x-vercel-mfe-local-proxy-origin" }]
1717
- };
1752
+ const proxyRedirects = [
1753
+ {
1754
+ source: "/:path*",
1755
+ destination: `http://localhost:${microfrontend.getLocalProxyPort()}/:path*`,
1756
+ permanent: false,
1757
+ missing: [{ type: "header", key: "x-vercel-mfe-local-proxy-origin" }]
1758
+ }
1759
+ ];
1718
1760
  if (next.redirects && typeof next.redirects === "function") {
1719
1761
  const originalRedirectsFn = next.redirects;
1720
1762
  next.redirects = async () => {
1721
1763
  const originalRedirects = await originalRedirectsFn();
1722
- return [proxyRedirect, ...originalRedirects];
1764
+ return [...proxyRedirects, ...originalRedirects];
1723
1765
  };
1724
1766
  } else {
1725
- next.redirects = async () => [proxyRedirect];
1767
+ next.redirects = async () => proxyRedirects;
1726
1768
  }
1727
1769
  }
1728
1770
  return { next };
1729
1771
  }
1730
1772
 
1773
+ // src/routing/get-domain-from-environment.ts
1774
+ function getDomainFromEnvironment({
1775
+ app,
1776
+ target
1777
+ }) {
1778
+ const mfeProjects = JSON.parse(
1779
+ process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1780
+ );
1781
+ if (mfeProjects.length === 0) {
1782
+ throw new Error("Missing related microfrontends project information");
1783
+ }
1784
+ if (!app.projectId) {
1785
+ throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1786
+ }
1787
+ const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
1788
+ if (!vercelProject) {
1789
+ throw new Error(
1790
+ `Missing related microfrontends project information for application "${app.name}"`
1791
+ );
1792
+ }
1793
+ const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1794
+ if (!domain) {
1795
+ throw new Error(
1796
+ `Missing domain for target "${target}" in application "${app.name}"`
1797
+ );
1798
+ }
1799
+ return domain.startsWith("https://") ? domain : `https://${domain}`;
1800
+ }
1801
+
1802
+ // src/routing/get-domain-for-current-environment.ts
1803
+ function debugDomains(zone, env, domain) {
1804
+ if (process.env.MFE_DEBUG === "true") {
1805
+ const indent = " ".repeat(4);
1806
+ const header = "domains (zone (env) -> domain)";
1807
+ const separator = "\u23AF".repeat(header.length);
1808
+ const line = `${indent} 1. ${zone} (${env}) -> ${domain}`;
1809
+ console.log(`${indent}${header}
1810
+ ${indent}${separator}
1811
+ ${line}
1812
+ `);
1813
+ }
1814
+ }
1815
+ function getCurrentEnvironment() {
1816
+ const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1817
+ const isPreview = process.env.VERCEL_ENV === "preview";
1818
+ const isProduction2 = process.env.VERCEL_ENV === "production";
1819
+ if (isDevelopment) {
1820
+ return { group: "development" };
1821
+ }
1822
+ if (isProduction2) {
1823
+ return { group: "production" };
1824
+ }
1825
+ if (isPreview) {
1826
+ return { group: "preview" };
1827
+ }
1828
+ return { group: "custom", name: process.env.VERCEL_ENV };
1829
+ }
1830
+ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1831
+ const app = config.getApplication(appName);
1832
+ if (!opts.ignoreOverride && app.overrides?.environment) {
1833
+ return app.overrides.environment.toString();
1834
+ }
1835
+ const { group } = getCurrentEnvironment();
1836
+ const fallbackHost = config.getDefaultApplication().fallback.toString();
1837
+ switch (group) {
1838
+ case "development": {
1839
+ const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : fallbackHost;
1840
+ debugDomains(appName, "development", domain);
1841
+ return domain;
1842
+ }
1843
+ case "preview": {
1844
+ return getDomainFromEnvironment({ app, target: "preview" });
1845
+ }
1846
+ case "production": {
1847
+ return getDomainFromEnvironment({ app, target: "production" });
1848
+ }
1849
+ case "custom":
1850
+ throw new Error(
1851
+ "Custom environments are not supported in getDomainForCurrentEnvironment"
1852
+ );
1853
+ }
1854
+ }
1855
+
1731
1856
  // src/next/config/transforms/rewrites.ts
1732
1857
  function debugRewrites(rewrites) {
1733
- if (process.env.MFE_DEBUG === "true") {
1858
+ if (process.env.MFE_DEBUG === "true" || process.env.MFE_DEBUG === "1") {
1734
1859
  const indent = " ".repeat(4);
1735
1860
  const header = "rewrites (source \u2192 destination)";
1736
1861
  const separator = "\u23AF".repeat(header.length);
@@ -1760,8 +1885,8 @@ function rewritesMapToArr(rewrites) {
1760
1885
  ];
1761
1886
  });
1762
1887
  }
1763
- function transform4(args) {
1764
- const { next, app } = args;
1888
+ function transform5(args) {
1889
+ const { app, microfrontend, next, opts } = args;
1765
1890
  const buildBeforeFiles = () => {
1766
1891
  const rewrites = /* @__PURE__ */ new Map();
1767
1892
  if (!app.isDefault()) {
@@ -1780,6 +1905,15 @@ function transform4(args) {
1780
1905
  pathname: "/_vercel/:path*"
1781
1906
  }
1782
1907
  });
1908
+ } else if (opts?.supportPagesRouter && !(microfrontend instanceof MicrofrontendChildConfig)) {
1909
+ for (const child of microfrontend.getChildApplications()) {
1910
+ rewrites.set(`/_next/data/${child.getAssetPrefix()}-:buildId/:path*`, {
1911
+ destination: {
1912
+ domain: getDomainForCurrentEnvironment(microfrontend, child.name),
1913
+ pathname: `/_next/data/${child.getAssetPrefix()}-:buildId/:path*`
1914
+ }
1915
+ });
1916
+ }
1783
1917
  }
1784
1918
  return rewritesMapToArr(rewrites);
1785
1919
  };
@@ -1815,89 +1949,6 @@ function transform4(args) {
1815
1949
  };
1816
1950
  }
1817
1951
 
1818
- // src/routing/get-domain-from-environment.ts
1819
- function getDomainFromEnvironment({
1820
- app,
1821
- target
1822
- }) {
1823
- const mfeProjects = JSON.parse(
1824
- process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1825
- );
1826
- if (mfeProjects.length === 0) {
1827
- throw new Error("Missing related microfrontends project information");
1828
- }
1829
- if (!app.projectId) {
1830
- throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1831
- }
1832
- const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
1833
- if (!vercelProject) {
1834
- throw new Error(
1835
- `Missing related microfrontends project information for application "${app.name}"`
1836
- );
1837
- }
1838
- const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1839
- if (!domain) {
1840
- throw new Error(
1841
- `Missing domain for target "${target}" in application "${app.name}"`
1842
- );
1843
- }
1844
- return domain.startsWith("https://") ? domain : `https://${domain}`;
1845
- }
1846
-
1847
- // src/routing/get-domain-for-current-environment.ts
1848
- function debugDomains(zone, env, domain) {
1849
- if (process.env.MFE_DEBUG === "true") {
1850
- const indent = " ".repeat(4);
1851
- const header = "domains (zone (env) -> domain)";
1852
- const separator = "\u23AF".repeat(header.length);
1853
- const line = `${indent} 1. ${zone} (${env}) -> ${domain}`;
1854
- console.log(`${indent}${header}
1855
- ${indent}${separator}
1856
- ${line}
1857
- `);
1858
- }
1859
- }
1860
- function getCurrentEnvironment() {
1861
- const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1862
- const isPreview = process.env.VERCEL_ENV === "preview";
1863
- const isProduction2 = process.env.VERCEL_ENV === "production";
1864
- if (isDevelopment) {
1865
- return { group: "development" };
1866
- }
1867
- if (isProduction2) {
1868
- return { group: "production" };
1869
- }
1870
- if (isPreview) {
1871
- return { group: "preview" };
1872
- }
1873
- return { group: "custom", name: process.env.VERCEL_ENV };
1874
- }
1875
- function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1876
- const app = config.getApplication(appName);
1877
- if (!opts.ignoreOverride && app.overrides?.environment) {
1878
- return app.overrides.environment.toString();
1879
- }
1880
- const { group } = getCurrentEnvironment();
1881
- const fallbackHost = config.getDefaultApplication().fallback.toString();
1882
- switch (group) {
1883
- case "development": {
1884
- const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : fallbackHost;
1885
- debugDomains(appName, "development", domain);
1886
- return domain;
1887
- }
1888
- case "preview": {
1889
- return getDomainFromEnvironment({ app, target: "preview" });
1890
- }
1891
- case "production": {
1892
- return getDomainFromEnvironment({ app, target: "production" });
1893
- }
1894
- case "custom":
1895
- throw new Error(
1896
- "Custom environments are not supported in getDomainForCurrentEnvironment"
1897
- );
1898
- }
1899
- }
1900
-
1901
1952
  // src/next/config/transforms/server-actions.ts
1902
1953
  function debugRewrites2(allowedOrigins) {
1903
1954
  if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
@@ -1918,7 +1969,7 @@ ${table}
1918
1969
  }
1919
1970
  }
1920
1971
  var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
1921
- function transform5(args) {
1972
+ function transform6(args) {
1922
1973
  const { next, app, microfrontend } = args;
1923
1974
  if (microfrontend instanceof MicrofrontendChildConfig) {
1924
1975
  console.warn(
@@ -1975,8 +2026,8 @@ function transform5(args) {
1975
2026
  }
1976
2027
 
1977
2028
  // src/next/config/transforms/webpack.ts
1978
- function transform6(args) {
1979
- const { next, microfrontend } = args;
2029
+ function transform7(args) {
2030
+ const { next, microfrontend, opts } = args;
1980
2031
  const configWithWebpack = {
1981
2032
  ...next,
1982
2033
  webpack(cfg, context) {
@@ -2007,6 +2058,38 @@ function transform6(args) {
2007
2058
  ...config.resolve.fallback
2008
2059
  };
2009
2060
  }
2061
+ if (opts?.supportPagesRouter) {
2062
+ config.optimization.moduleIds = "deterministic";
2063
+ config.optimization.chunkIds = "deterministic";
2064
+ config.plugins.push({
2065
+ apply: (compiler) => {
2066
+ compiler.hooks.compilation.tap(
2067
+ "SortChunksPlugin",
2068
+ (compilation) => {
2069
+ compilation.hooks.optimizeChunks.tap(
2070
+ "SortChunksPlugin",
2071
+ (chunks) => {
2072
+ const sortedChunks = Array.from(chunks).sort((a, b) => {
2073
+ if (a.name && b.name) {
2074
+ return a.name.localeCompare(b.name);
2075
+ }
2076
+ return (String(a.id) || "").localeCompare(
2077
+ String(b.id) || ""
2078
+ );
2079
+ });
2080
+ Array.from(chunks).forEach((chunk) => {
2081
+ compilation.chunks.delete(chunk);
2082
+ });
2083
+ sortedChunks.forEach((chunk) => {
2084
+ compilation.chunks.add(chunk);
2085
+ });
2086
+ }
2087
+ );
2088
+ }
2089
+ );
2090
+ }
2091
+ });
2092
+ }
2010
2093
  return config;
2011
2094
  }
2012
2095
  };
@@ -2018,11 +2101,12 @@ function transform6(args) {
2018
2101
  // src/next/config/transforms/index.ts
2019
2102
  var transforms = {
2020
2103
  assetPrefix: transform,
2021
- draftMode: transform2,
2022
- redirects: transform3,
2023
- rewrites: transform4,
2024
- serverActions: transform5,
2025
- webpack: transform6
2104
+ buildId: transform2,
2105
+ draftMode: transform3,
2106
+ redirects: transform4,
2107
+ rewrites: transform5,
2108
+ serverActions: transform6,
2109
+ webpack: transform7
2026
2110
  };
2027
2111
 
2028
2112
  // src/next/config/env.ts
@@ -2090,18 +2174,19 @@ function withMicrofrontends(nextConfig, opts) {
2090
2174
  const app = microfrontends.config.getApplication(fromApp);
2091
2175
  setEnvironment({ app, microfrontends });
2092
2176
  let next = { ...nextConfig };
2093
- for (const [key, transform7] of typedEntries(transforms)) {
2177
+ for (const [key, transform8] of typedEntries(transforms)) {
2094
2178
  if (opts?.skipTransforms?.includes(key)) {
2095
2179
  console.log(`Skipping ${key} transform`);
2096
2180
  continue;
2097
2181
  }
2098
2182
  try {
2099
- const transformedConfig = transform7({
2183
+ const transformedConfig = transform8({
2100
2184
  app,
2101
2185
  next,
2102
2186
  microfrontend: microfrontends.config,
2103
2187
  opts: {
2104
- isProduction: isProduction(opts)
2188
+ isProduction: isProduction(opts),
2189
+ supportPagesRouter: opts?.supportPagesRouter
2105
2190
  }
2106
2191
  });
2107
2192
  next = transformedConfig.next;