@vercel/microfrontends 1.0.1-canary.1 → 1.0.1-canary.3

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/dist/bin/cli.cjs +43 -33
  2. package/dist/config.cjs +41 -32
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +1 -1
  5. package/dist/config.js +41 -32
  6. package/dist/config.js.map +1 -1
  7. package/dist/experimental/sveltekit.cjs +41 -32
  8. package/dist/experimental/sveltekit.cjs.map +1 -1
  9. package/dist/experimental/sveltekit.js +41 -32
  10. package/dist/experimental/sveltekit.js.map +1 -1
  11. package/dist/experimental/vite.cjs +88 -46
  12. package/dist/experimental/vite.cjs.map +1 -1
  13. package/dist/experimental/vite.d.ts +1 -1
  14. package/dist/experimental/vite.js +88 -48
  15. package/dist/experimental/vite.js.map +1 -1
  16. package/dist/{index-d5994ac5.d.ts → index-2b59c627.d.ts} +1 -1
  17. package/dist/microfrontends/server.cjs +41 -32
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +1 -1
  20. package/dist/microfrontends/server.js +41 -32
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends.cjs +41 -32
  23. package/dist/microfrontends.cjs.map +1 -1
  24. package/dist/microfrontends.d.ts +1 -1
  25. package/dist/microfrontends.js +41 -32
  26. package/dist/microfrontends.js.map +1 -1
  27. package/dist/next/config.cjs +205 -131
  28. package/dist/next/config.cjs.map +1 -1
  29. package/dist/next/config.d.ts +7 -1
  30. package/dist/next/config.js +205 -131
  31. package/dist/next/config.js.map +1 -1
  32. package/dist/next/middleware.cjs +41 -32
  33. package/dist/next/middleware.cjs.map +1 -1
  34. package/dist/next/middleware.js +41 -32
  35. package/dist/next/middleware.js.map +1 -1
  36. package/dist/next/testing.cjs +41 -32
  37. package/dist/next/testing.cjs.map +1 -1
  38. package/dist/next/testing.d.ts +2 -2
  39. package/dist/next/testing.js +42 -33
  40. package/dist/next/testing.js.map +1 -1
  41. package/dist/utils/mfe-port.cjs +41 -32
  42. package/dist/utils/mfe-port.cjs.map +1 -1
  43. package/dist/utils/mfe-port.js +41 -32
  44. package/dist/utils/mfe-port.js.map +1 -1
  45. 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
  }
@@ -667,12 +680,8 @@ var MicrofrontendConfigIsomorphic = class {
667
680
  const skipValidation = opts?.skipValidation ?? [];
668
681
  const c = typeof config === "string" ? parse(config) : config;
669
682
  if (isMainConfig(c)) {
670
- if (!skipValidation.includes("paths")) {
671
- validateConfigPaths(c.applications);
672
- }
673
- if (!skipValidation.includes("defaultApplication")) {
674
- validateConfigDefaultApplication(c.applications);
675
- }
683
+ validateConfigPaths(c.applications);
684
+ validateConfigDefaultApplication(c.applications);
676
685
  if (!skipValidation.includes("deprecatedFields")) {
677
686
  validateDeprecatedFields(c);
678
687
  }
@@ -1687,8 +1696,30 @@ function transform(args) {
1687
1696
  };
1688
1697
  }
1689
1698
 
1690
- // src/next/config/transforms/draft-mode.ts
1699
+ // src/next/config/transforms/build-id.ts
1700
+ import { nanoid } from "nanoid";
1691
1701
  function transform2(args) {
1702
+ const { app, next, opts } = args;
1703
+ if (!opts?.supportPagesRouter) {
1704
+ return { next };
1705
+ }
1706
+ if (next.generateBuildId !== void 0) {
1707
+ throw new Error(
1708
+ '"generateBuildId" must not already be set in next.config.js when using microfrontends with "supportPagesRouter"'
1709
+ );
1710
+ }
1711
+ if (!app.isDefault()) {
1712
+ next.generateBuildId = () => {
1713
+ return `${app.getAssetPrefix()}-${nanoid()}`;
1714
+ };
1715
+ }
1716
+ return {
1717
+ next
1718
+ };
1719
+ }
1720
+
1721
+ // src/next/config/transforms/draft-mode.ts
1722
+ function transform3(args) {
1692
1723
  const { next } = args;
1693
1724
  if (next.experimental?.multiZoneDraftMode !== void 0) {
1694
1725
  return {
@@ -1709,7 +1740,7 @@ function routeToLocalProxy() {
1709
1740
  }
1710
1741
 
1711
1742
  // src/next/config/transforms/redirects.ts
1712
- function transform3(args) {
1743
+ function transform4(args) {
1713
1744
  const { next, microfrontend, opts } = args;
1714
1745
  const isProduction2 = opts?.isProduction ?? false;
1715
1746
  const requireLocalProxyHeader = routeToLocalProxy() && !isProduction2 && !process.env.MFE_DISABLE_LOCAL_PROXY_REWRITE;
@@ -1735,9 +1766,92 @@ function transform3(args) {
1735
1766
  return { next };
1736
1767
  }
1737
1768
 
1769
+ // src/routing/get-domain-from-environment.ts
1770
+ function getDomainFromEnvironment({
1771
+ app,
1772
+ target
1773
+ }) {
1774
+ const mfeProjects = JSON.parse(
1775
+ process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1776
+ );
1777
+ if (mfeProjects.length === 0) {
1778
+ throw new Error("Missing related microfrontends project information");
1779
+ }
1780
+ if (!app.projectId) {
1781
+ throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1782
+ }
1783
+ const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
1784
+ if (!vercelProject) {
1785
+ throw new Error(
1786
+ `Missing related microfrontends project information for application "${app.name}"`
1787
+ );
1788
+ }
1789
+ const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1790
+ if (!domain) {
1791
+ throw new Error(
1792
+ `Missing domain for target "${target}" in application "${app.name}"`
1793
+ );
1794
+ }
1795
+ return domain.startsWith("https://") ? domain : `https://${domain}`;
1796
+ }
1797
+
1798
+ // src/routing/get-domain-for-current-environment.ts
1799
+ function debugDomains(zone, env, domain) {
1800
+ if (process.env.MFE_DEBUG === "true") {
1801
+ const indent = " ".repeat(4);
1802
+ const header = "domains (zone (env) -> domain)";
1803
+ const separator = "\u23AF".repeat(header.length);
1804
+ const line = `${indent} 1. ${zone} (${env}) -> ${domain}`;
1805
+ console.log(`${indent}${header}
1806
+ ${indent}${separator}
1807
+ ${line}
1808
+ `);
1809
+ }
1810
+ }
1811
+ function getCurrentEnvironment() {
1812
+ const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1813
+ const isPreview = process.env.VERCEL_ENV === "preview";
1814
+ const isProduction2 = process.env.VERCEL_ENV === "production";
1815
+ if (isDevelopment) {
1816
+ return { group: "development" };
1817
+ }
1818
+ if (isProduction2) {
1819
+ return { group: "production" };
1820
+ }
1821
+ if (isPreview) {
1822
+ return { group: "preview" };
1823
+ }
1824
+ return { group: "custom", name: process.env.VERCEL_ENV };
1825
+ }
1826
+ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1827
+ const app = config.getApplication(appName);
1828
+ if (!opts.ignoreOverride && app.overrides?.environment) {
1829
+ return app.overrides.environment.toString();
1830
+ }
1831
+ const { group } = getCurrentEnvironment();
1832
+ const fallbackHost = config.getDefaultApplication().fallback.toString();
1833
+ switch (group) {
1834
+ case "development": {
1835
+ const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : fallbackHost;
1836
+ debugDomains(appName, "development", domain);
1837
+ return domain;
1838
+ }
1839
+ case "preview": {
1840
+ return getDomainFromEnvironment({ app, target: "preview" });
1841
+ }
1842
+ case "production": {
1843
+ return getDomainFromEnvironment({ app, target: "production" });
1844
+ }
1845
+ case "custom":
1846
+ throw new Error(
1847
+ "Custom environments are not supported in getDomainForCurrentEnvironment"
1848
+ );
1849
+ }
1850
+ }
1851
+
1738
1852
  // src/next/config/transforms/rewrites.ts
1739
1853
  function debugRewrites(rewrites) {
1740
- if (process.env.MFE_DEBUG === "true") {
1854
+ if (process.env.MFE_DEBUG === "true" || process.env.MFE_DEBUG === "1") {
1741
1855
  const indent = " ".repeat(4);
1742
1856
  const header = "rewrites (source \u2192 destination)";
1743
1857
  const separator = "\u23AF".repeat(header.length);
@@ -1767,8 +1881,8 @@ function rewritesMapToArr(rewrites) {
1767
1881
  ];
1768
1882
  });
1769
1883
  }
1770
- function transform4(args) {
1771
- const { next, app } = args;
1884
+ function transform5(args) {
1885
+ const { app, microfrontend, next, opts } = args;
1772
1886
  const buildBeforeFiles = () => {
1773
1887
  const rewrites = /* @__PURE__ */ new Map();
1774
1888
  if (!app.isDefault()) {
@@ -1787,6 +1901,15 @@ function transform4(args) {
1787
1901
  pathname: "/_vercel/:path*"
1788
1902
  }
1789
1903
  });
1904
+ } else if (opts?.supportPagesRouter && !(microfrontend instanceof MicrofrontendChildConfig)) {
1905
+ for (const child of microfrontend.getChildApplications()) {
1906
+ rewrites.set(`/_next/data/${child.getAssetPrefix()}-:buildId/:path*`, {
1907
+ destination: {
1908
+ domain: getDomainForCurrentEnvironment(microfrontend, child.name),
1909
+ pathname: `/_next/data/${child.getAssetPrefix()}-:buildId/:path*`
1910
+ }
1911
+ });
1912
+ }
1790
1913
  }
1791
1914
  return rewritesMapToArr(rewrites);
1792
1915
  };
@@ -1822,89 +1945,6 @@ function transform4(args) {
1822
1945
  };
1823
1946
  }
1824
1947
 
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
1948
  // src/next/config/transforms/server-actions.ts
1909
1949
  function debugRewrites2(allowedOrigins) {
1910
1950
  if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
@@ -1925,7 +1965,7 @@ ${table}
1925
1965
  }
1926
1966
  }
1927
1967
  var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
1928
- function transform5(args) {
1968
+ function transform6(args) {
1929
1969
  const { next, app, microfrontend } = args;
1930
1970
  if (microfrontend instanceof MicrofrontendChildConfig) {
1931
1971
  console.warn(
@@ -1982,8 +2022,8 @@ function transform5(args) {
1982
2022
  }
1983
2023
 
1984
2024
  // src/next/config/transforms/webpack.ts
1985
- function transform6(args) {
1986
- const { next, microfrontend } = args;
2025
+ function transform7(args) {
2026
+ const { next, microfrontend, opts } = args;
1987
2027
  const configWithWebpack = {
1988
2028
  ...next,
1989
2029
  webpack(cfg, context) {
@@ -2014,6 +2054,38 @@ function transform6(args) {
2014
2054
  ...config.resolve.fallback
2015
2055
  };
2016
2056
  }
2057
+ if (opts?.supportPagesRouter) {
2058
+ config.optimization.moduleIds = "deterministic";
2059
+ config.optimization.chunkIds = "deterministic";
2060
+ config.plugins.push({
2061
+ apply: (compiler) => {
2062
+ compiler.hooks.compilation.tap(
2063
+ "SortChunksPlugin",
2064
+ (compilation) => {
2065
+ compilation.hooks.optimizeChunks.tap(
2066
+ "SortChunksPlugin",
2067
+ (chunks) => {
2068
+ const sortedChunks = Array.from(chunks).sort((a, b) => {
2069
+ if (a.name && b.name) {
2070
+ return a.name.localeCompare(b.name);
2071
+ }
2072
+ return (String(a.id) || "").localeCompare(
2073
+ String(b.id) || ""
2074
+ );
2075
+ });
2076
+ Array.from(chunks).forEach((chunk) => {
2077
+ compilation.chunks.delete(chunk);
2078
+ });
2079
+ sortedChunks.forEach((chunk) => {
2080
+ compilation.chunks.add(chunk);
2081
+ });
2082
+ }
2083
+ );
2084
+ }
2085
+ );
2086
+ }
2087
+ });
2088
+ }
2017
2089
  return config;
2018
2090
  }
2019
2091
  };
@@ -2025,11 +2097,12 @@ function transform6(args) {
2025
2097
  // src/next/config/transforms/index.ts
2026
2098
  var transforms = {
2027
2099
  assetPrefix: transform,
2028
- draftMode: transform2,
2029
- redirects: transform3,
2030
- rewrites: transform4,
2031
- serverActions: transform5,
2032
- webpack: transform6
2100
+ buildId: transform2,
2101
+ draftMode: transform3,
2102
+ redirects: transform4,
2103
+ rewrites: transform5,
2104
+ serverActions: transform6,
2105
+ webpack: transform7
2033
2106
  };
2034
2107
 
2035
2108
  // src/next/config/env.ts
@@ -2097,18 +2170,19 @@ function withMicrofrontends(nextConfig, opts) {
2097
2170
  const app = microfrontends.config.getApplication(fromApp);
2098
2171
  setEnvironment({ app, microfrontends });
2099
2172
  let next = { ...nextConfig };
2100
- for (const [key, transform7] of typedEntries(transforms)) {
2173
+ for (const [key, transform8] of typedEntries(transforms)) {
2101
2174
  if (opts?.skipTransforms?.includes(key)) {
2102
2175
  console.log(`Skipping ${key} transform`);
2103
2176
  continue;
2104
2177
  }
2105
2178
  try {
2106
- const transformedConfig = transform7({
2179
+ const transformedConfig = transform8({
2107
2180
  app,
2108
2181
  next,
2109
2182
  microfrontend: microfrontends.config,
2110
2183
  opts: {
2111
- isProduction: isProduction(opts)
2184
+ isProduction: isProduction(opts),
2185
+ supportPagesRouter: opts?.supportPagesRouter
2112
2186
  }
2113
2187
  });
2114
2188
  next = transformedConfig.next;