@vercel/microfrontends 1.0.1-canary.1 → 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 (40) hide show
  1. package/dist/bin/cli.cjs +41 -27
  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/experimental/sveltekit.cjs +39 -26
  7. package/dist/experimental/sveltekit.cjs.map +1 -1
  8. package/dist/experimental/sveltekit.js +39 -26
  9. package/dist/experimental/sveltekit.js.map +1 -1
  10. package/dist/experimental/vite.cjs +39 -26
  11. package/dist/experimental/vite.cjs.map +1 -1
  12. package/dist/experimental/vite.js +39 -26
  13. package/dist/experimental/vite.js.map +1 -1
  14. package/dist/microfrontends/server.cjs +39 -26
  15. package/dist/microfrontends/server.cjs.map +1 -1
  16. package/dist/microfrontends/server.js +39 -26
  17. package/dist/microfrontends/server.js.map +1 -1
  18. package/dist/microfrontends.cjs +39 -26
  19. package/dist/microfrontends.cjs.map +1 -1
  20. package/dist/microfrontends.js +39 -26
  21. package/dist/microfrontends.js.map +1 -1
  22. package/dist/next/config.cjs +203 -125
  23. package/dist/next/config.cjs.map +1 -1
  24. package/dist/next/config.d.ts +7 -1
  25. package/dist/next/config.js +203 -125
  26. package/dist/next/config.js.map +1 -1
  27. package/dist/next/middleware.cjs +39 -26
  28. package/dist/next/middleware.cjs.map +1 -1
  29. package/dist/next/middleware.js +39 -26
  30. package/dist/next/middleware.js.map +1 -1
  31. package/dist/next/testing.cjs +39 -26
  32. package/dist/next/testing.cjs.map +1 -1
  33. package/dist/next/testing.d.ts +1 -1
  34. package/dist/next/testing.js +40 -27
  35. package/dist/next/testing.js.map +1 -1
  36. package/dist/utils/mfe-port.cjs +39 -26
  37. package/dist/utils/mfe-port.cjs.map +1 -1
  38. package/dist/utils/mfe-port.js +39 -26
  39. package/dist/utils/mfe-port.js.map +1 -1
  40. package/package.json +2 -1
@@ -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 {
@@ -1709,7 +1744,7 @@ function routeToLocalProxy() {
1709
1744
  }
1710
1745
 
1711
1746
  // src/next/config/transforms/redirects.ts
1712
- function transform3(args) {
1747
+ function transform4(args) {
1713
1748
  const { next, microfrontend, opts } = args;
1714
1749
  const isProduction2 = opts?.isProduction ?? false;
1715
1750
  const requireLocalProxyHeader = routeToLocalProxy() && !isProduction2 && !process.env.MFE_DISABLE_LOCAL_PROXY_REWRITE;
@@ -1735,9 +1770,92 @@ function transform3(args) {
1735
1770
  return { next };
1736
1771
  }
1737
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
+
1738
1856
  // src/next/config/transforms/rewrites.ts
1739
1857
  function debugRewrites(rewrites) {
1740
- if (process.env.MFE_DEBUG === "true") {
1858
+ if (process.env.MFE_DEBUG === "true" || process.env.MFE_DEBUG === "1") {
1741
1859
  const indent = " ".repeat(4);
1742
1860
  const header = "rewrites (source \u2192 destination)";
1743
1861
  const separator = "\u23AF".repeat(header.length);
@@ -1767,8 +1885,8 @@ function rewritesMapToArr(rewrites) {
1767
1885
  ];
1768
1886
  });
1769
1887
  }
1770
- function transform4(args) {
1771
- const { next, app } = args;
1888
+ function transform5(args) {
1889
+ const { app, microfrontend, next, opts } = args;
1772
1890
  const buildBeforeFiles = () => {
1773
1891
  const rewrites = /* @__PURE__ */ new Map();
1774
1892
  if (!app.isDefault()) {
@@ -1787,6 +1905,15 @@ function transform4(args) {
1787
1905
  pathname: "/_vercel/:path*"
1788
1906
  }
1789
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
+ }
1790
1917
  }
1791
1918
  return rewritesMapToArr(rewrites);
1792
1919
  };
@@ -1822,89 +1949,6 @@ function transform4(args) {
1822
1949
  };
1823
1950
  }
1824
1951
 
1825
- // src/routing/get-domain-from-environment.ts
1826
- function getDomainFromEnvironment({
1827
- app,
1828
- target
1829
- }) {
1830
- const mfeProjects = JSON.parse(
1831
- process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1832
- );
1833
- if (mfeProjects.length === 0) {
1834
- throw new Error("Missing related microfrontends project information");
1835
- }
1836
- if (!app.projectId) {
1837
- throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1838
- }
1839
- const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
1840
- if (!vercelProject) {
1841
- throw new Error(
1842
- `Missing related microfrontends project information for application "${app.name}"`
1843
- );
1844
- }
1845
- const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1846
- if (!domain) {
1847
- throw new Error(
1848
- `Missing domain for target "${target}" in application "${app.name}"`
1849
- );
1850
- }
1851
- return domain.startsWith("https://") ? domain : `https://${domain}`;
1852
- }
1853
-
1854
- // src/routing/get-domain-for-current-environment.ts
1855
- function debugDomains(zone, env, domain) {
1856
- if (process.env.MFE_DEBUG === "true") {
1857
- const indent = " ".repeat(4);
1858
- const header = "domains (zone (env) -> domain)";
1859
- const separator = "\u23AF".repeat(header.length);
1860
- const line = `${indent} 1. ${zone} (${env}) -> ${domain}`;
1861
- console.log(`${indent}${header}
1862
- ${indent}${separator}
1863
- ${line}
1864
- `);
1865
- }
1866
- }
1867
- function getCurrentEnvironment() {
1868
- const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1869
- const isPreview = process.env.VERCEL_ENV === "preview";
1870
- const isProduction2 = process.env.VERCEL_ENV === "production";
1871
- if (isDevelopment) {
1872
- return { group: "development" };
1873
- }
1874
- if (isProduction2) {
1875
- return { group: "production" };
1876
- }
1877
- if (isPreview) {
1878
- return { group: "preview" };
1879
- }
1880
- return { group: "custom", name: process.env.VERCEL_ENV };
1881
- }
1882
- function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1883
- const app = config.getApplication(appName);
1884
- if (!opts.ignoreOverride && app.overrides?.environment) {
1885
- return app.overrides.environment.toString();
1886
- }
1887
- const { group } = getCurrentEnvironment();
1888
- const fallbackHost = config.getDefaultApplication().fallback.toString();
1889
- switch (group) {
1890
- case "development": {
1891
- const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : fallbackHost;
1892
- debugDomains(appName, "development", domain);
1893
- return domain;
1894
- }
1895
- case "preview": {
1896
- return getDomainFromEnvironment({ app, target: "preview" });
1897
- }
1898
- case "production": {
1899
- return getDomainFromEnvironment({ app, target: "production" });
1900
- }
1901
- case "custom":
1902
- throw new Error(
1903
- "Custom environments are not supported in getDomainForCurrentEnvironment"
1904
- );
1905
- }
1906
- }
1907
-
1908
1952
  // src/next/config/transforms/server-actions.ts
1909
1953
  function debugRewrites2(allowedOrigins) {
1910
1954
  if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
@@ -1925,7 +1969,7 @@ ${table}
1925
1969
  }
1926
1970
  }
1927
1971
  var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
1928
- function transform5(args) {
1972
+ function transform6(args) {
1929
1973
  const { next, app, microfrontend } = args;
1930
1974
  if (microfrontend instanceof MicrofrontendChildConfig) {
1931
1975
  console.warn(
@@ -1982,8 +2026,8 @@ function transform5(args) {
1982
2026
  }
1983
2027
 
1984
2028
  // src/next/config/transforms/webpack.ts
1985
- function transform6(args) {
1986
- const { next, microfrontend } = args;
2029
+ function transform7(args) {
2030
+ const { next, microfrontend, opts } = args;
1987
2031
  const configWithWebpack = {
1988
2032
  ...next,
1989
2033
  webpack(cfg, context) {
@@ -2014,6 +2058,38 @@ function transform6(args) {
2014
2058
  ...config.resolve.fallback
2015
2059
  };
2016
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
+ }
2017
2093
  return config;
2018
2094
  }
2019
2095
  };
@@ -2025,11 +2101,12 @@ function transform6(args) {
2025
2101
  // src/next/config/transforms/index.ts
2026
2102
  var transforms = {
2027
2103
  assetPrefix: transform,
2028
- draftMode: transform2,
2029
- redirects: transform3,
2030
- rewrites: transform4,
2031
- serverActions: transform5,
2032
- webpack: transform6
2104
+ buildId: transform2,
2105
+ draftMode: transform3,
2106
+ redirects: transform4,
2107
+ rewrites: transform5,
2108
+ serverActions: transform6,
2109
+ webpack: transform7
2033
2110
  };
2034
2111
 
2035
2112
  // src/next/config/env.ts
@@ -2097,18 +2174,19 @@ function withMicrofrontends(nextConfig, opts) {
2097
2174
  const app = microfrontends.config.getApplication(fromApp);
2098
2175
  setEnvironment({ app, microfrontends });
2099
2176
  let next = { ...nextConfig };
2100
- for (const [key, transform7] of typedEntries(transforms)) {
2177
+ for (const [key, transform8] of typedEntries(transforms)) {
2101
2178
  if (opts?.skipTransforms?.includes(key)) {
2102
2179
  console.log(`Skipping ${key} transform`);
2103
2180
  continue;
2104
2181
  }
2105
2182
  try {
2106
- const transformedConfig = transform7({
2183
+ const transformedConfig = transform8({
2107
2184
  app,
2108
2185
  next,
2109
2186
  microfrontend: microfrontends.config,
2110
2187
  opts: {
2111
- isProduction: isProduction(opts)
2188
+ isProduction: isProduction(opts),
2189
+ supportPagesRouter: opts?.supportPagesRouter
2112
2190
  }
2113
2191
  });
2114
2192
  next = transformedConfig.next;