@vercel/microfrontends 1.0.1-canary.2 → 1.0.1-canary.4

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 +115 -85
  2. package/dist/config.cjs +5 -6
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +1 -1
  5. package/dist/config.js +5 -6
  6. package/dist/config.js.map +1 -1
  7. package/dist/experimental/sveltekit.cjs +81 -78
  8. package/dist/experimental/sveltekit.cjs.map +1 -1
  9. package/dist/experimental/sveltekit.js +81 -78
  10. package/dist/experimental/sveltekit.js.map +1 -1
  11. package/dist/experimental/vite.cjs +124 -88
  12. package/dist/experimental/vite.cjs.map +1 -1
  13. package/dist/experimental/vite.d.ts +1 -1
  14. package/dist/experimental/vite.js +124 -90
  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 +81 -51
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +1 -1
  20. package/dist/microfrontends/server.js +81 -51
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends.cjs +5 -6
  23. package/dist/microfrontends.cjs.map +1 -1
  24. package/dist/microfrontends.d.ts +1 -1
  25. package/dist/microfrontends.js +5 -6
  26. package/dist/microfrontends.js.map +1 -1
  27. package/dist/next/config.cjs +81 -78
  28. package/dist/next/config.cjs.map +1 -1
  29. package/dist/next/config.js +81 -78
  30. package/dist/next/config.js.map +1 -1
  31. package/dist/next/middleware.cjs +5 -6
  32. package/dist/next/middleware.cjs.map +1 -1
  33. package/dist/next/middleware.js +5 -6
  34. package/dist/next/middleware.js.map +1 -1
  35. package/dist/next/testing.cjs +5 -6
  36. package/dist/next/testing.cjs.map +1 -1
  37. package/dist/next/testing.d.ts +1 -1
  38. package/dist/next/testing.js +5 -6
  39. package/dist/next/testing.js.map +1 -1
  40. package/dist/utils/mfe-port.cjs +85 -55
  41. package/dist/utils/mfe-port.cjs.map +1 -1
  42. package/dist/utils/mfe-port.js +85 -55
  43. package/dist/utils/mfe-port.js.map +1 -1
  44. package/package.json +2 -2
@@ -1,11 +1,6 @@
1
- // src/vite/index.ts
2
- import { existsSync } from "node:fs";
3
- import { join as join3 } from "node:path";
4
- import { cwd } from "node:process";
5
-
6
1
  // src/config/microfrontends/server/index.ts
7
- import fs5 from "node:fs";
8
- import { dirname as dirname3, join as join2 } from "node:path";
2
+ import fs6 from "node:fs";
3
+ import { dirname as dirname3 } from "node:path";
9
4
 
10
5
  // src/config/overrides/constants.ts
11
6
  var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
@@ -188,21 +183,21 @@ var MicrofrontendConfigClient = class {
188
183
  isEqual(other) {
189
184
  return JSON.stringify(this.applications) === JSON.stringify(other.applications);
190
185
  }
191
- getApplicationNameForPath(path5) {
192
- if (!path5.startsWith("/")) {
186
+ getApplicationNameForPath(path6) {
187
+ if (!path6.startsWith("/")) {
193
188
  throw new Error(`Path must start with a /`);
194
189
  }
195
- if (this.pathCache[path5]) {
196
- return this.pathCache[path5];
190
+ if (this.pathCache[path6]) {
191
+ return this.pathCache[path6];
197
192
  }
198
- const pathname = new URL(path5, "https://example.com").pathname;
193
+ const pathname = new URL(path6, "https://example.com").pathname;
199
194
  for (const [name, application] of Object.entries(this.applications)) {
200
195
  if (application.routing) {
201
196
  for (const group of application.routing) {
202
197
  for (const childPath of group.paths) {
203
198
  const regexp = pathToRegexp(childPath);
204
199
  if (regexp.test(pathname)) {
205
- this.pathCache[path5] = name;
200
+ this.pathCache[path6] = name;
206
201
  return name;
207
202
  }
208
203
  }
@@ -215,7 +210,7 @@ var MicrofrontendConfigClient = class {
215
210
  if (!defaultApplication) {
216
211
  return null;
217
212
  }
218
- this.pathCache[path5] = defaultApplication[0];
213
+ this.pathCache[path6] = defaultApplication[0];
219
214
  return defaultApplication[0];
220
215
  }
221
216
  serialize() {
@@ -237,18 +232,18 @@ var validateConfigPaths = (applicationConfigsById) => {
237
232
  }
238
233
  const childApp = app;
239
234
  for (const pathMatch of childApp.routing) {
240
- for (const path5 of pathMatch.paths) {
241
- const maybeError = validatePathExpression(path5);
235
+ for (const path6 of pathMatch.paths) {
236
+ const maybeError = validatePathExpression(path6);
242
237
  if (maybeError) {
243
238
  errors.push(maybeError);
244
239
  } else {
245
- const existing = pathsByApplicationId.get(path5);
240
+ const existing = pathsByApplicationId.get(path6);
246
241
  if (existing) {
247
242
  existing.applications.push(id);
248
243
  } else {
249
- pathsByApplicationId.set(path5, {
244
+ pathsByApplicationId.set(path6, {
250
245
  applications: [id],
251
- matcher: pathToRegexp2(path5),
246
+ matcher: pathToRegexp2(path6),
252
247
  applicationId: id
253
248
  });
254
249
  }
@@ -257,24 +252,24 @@ var validateConfigPaths = (applicationConfigsById) => {
257
252
  }
258
253
  }
259
254
  const entries = Array.from(pathsByApplicationId.entries());
260
- for (const [path5, { applications: ids, matcher, applicationId }] of entries) {
255
+ for (const [path6, { applications: ids, matcher, applicationId }] of entries) {
261
256
  if (ids.length > 1) {
262
257
  errors.push(
263
- `Duplicate path "${path5}" for applications "${ids.join(", ")}"`
258
+ `Duplicate path "${path6}" for applications "${ids.join(", ")}"`
264
259
  );
265
260
  }
266
261
  for (const [
267
262
  matchPath,
268
263
  { applications: matchIds, applicationId: matchApplicationId }
269
264
  ] of entries) {
270
- if (path5 === matchPath) {
265
+ if (path6 === matchPath) {
271
266
  continue;
272
267
  }
273
268
  if (applicationId === matchApplicationId) {
274
269
  continue;
275
270
  }
276
271
  if (matcher.test(matchPath)) {
277
- const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
272
+ const source = `"${path6}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
278
273
  const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
279
274
  errors.push(
280
275
  `Overlapping path detected between ${source} and ${destination}`
@@ -290,39 +285,42 @@ var validateConfigPaths = (applicationConfigsById) => {
290
285
  }
291
286
  };
292
287
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
293
- function validatePathExpression(path5) {
288
+ function validatePathExpression(path6) {
294
289
  try {
295
- const tokens = parsePathRegexp(path5);
296
- if (/(?<!\\)\{/.test(path5)) {
297
- return `Optional paths are not supported: ${path5}`;
290
+ const tokens = parsePathRegexp(path6);
291
+ if (/(?<!\\)\{/.test(path6)) {
292
+ return `Optional paths are not supported: ${path6}`;
298
293
  }
299
- if (/(?<!\\|\()\?/.test(path5)) {
300
- return `Optional paths are not supported: ${path5}`;
294
+ if (/(?<!\\|\()\?/.test(path6)) {
295
+ return `Optional paths are not supported: ${path6}`;
301
296
  }
302
- if (/\/[^/]*(?<!\\):[^/]*(?<!\\):[^/]*/.test(path5)) {
303
- return `Only one wildcard is allowed per path segment: ${path5}`;
297
+ if (/\/[^/]*(?<!\\):[^/]*(?<!\\):[^/]*/.test(path6)) {
298
+ return `Only one wildcard is allowed per path segment: ${path6}`;
304
299
  }
305
300
  for (let i = 0; i < tokens.length; i++) {
306
301
  const token = tokens[i];
307
302
  if (token === void 0) {
308
- return `token ${i} in ${path5} is undefined, this shouldn't happen`;
303
+ return `token ${i} in ${path6} is undefined, this shouldn't happen`;
309
304
  }
310
305
  if (typeof token !== "string") {
306
+ if (!token.name) {
307
+ return `Only named wildcards are allowed: ${path6} (hint: add ":path" to the wildcard)`;
308
+ }
311
309
  if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
312
310
  // Only limited regex is supported for now, due to performance considerations
313
311
  !/^(?<allowed>[\w]+(?:\|[^|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^|()]+)+)\)\.\*$/.test(
314
312
  token.pattern
315
313
  )) {
316
- return `Path ${path5} cannot use unsupported regular expression wildcard`;
314
+ return `Path ${path6} cannot use unsupported regular expression wildcard`;
317
315
  }
318
316
  if (token.modifier && i !== tokens.length - 1) {
319
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path5}. Modifiers are only allowed in the last path component`;
317
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path6}. Modifiers are only allowed in the last path component`;
320
318
  }
321
319
  }
322
320
  }
323
321
  } catch (e) {
324
322
  const message = e instanceof Error ? e.message : String(e);
325
- return `Path ${path5} could not be parsed into regexp: ${message}`;
323
+ return `Path ${path6} could not be parsed into regexp: ${message}`;
326
324
  }
327
325
  return void 0;
328
326
  }
@@ -676,12 +674,8 @@ var MicrofrontendConfigIsomorphic = class {
676
674
  const skipValidation = opts?.skipValidation ?? [];
677
675
  const c = typeof config === "string" ? parse(config) : config;
678
676
  if (isMainConfig(c)) {
679
- if (!skipValidation.includes("paths")) {
680
- validateConfigPaths(c.applications);
681
- }
682
- if (!skipValidation.includes("defaultApplication")) {
683
- validateConfigDefaultApplication(c.applications);
684
- }
677
+ validateConfigPaths(c.applications);
678
+ validateConfigDefaultApplication(c.applications);
685
679
  if (!skipValidation.includes("deprecatedFields")) {
686
680
  validateDeprecatedFields(c);
687
681
  }
@@ -1038,6 +1032,9 @@ function isMonorepo({
1038
1032
  if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
1039
1033
  return true;
1040
1034
  }
1035
+ if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
1036
+ return true;
1037
+ }
1041
1038
  const packageJsonPath = path2.join(repositoryRoot, "package.json");
1042
1039
  if (!fs2.existsSync(packageJsonPath)) {
1043
1040
  return false;
@@ -1083,8 +1080,42 @@ function findConfig({ dir }) {
1083
1080
  return null;
1084
1081
  }
1085
1082
 
1086
- // src/config/microfrontends/server/utils/get-output-file-path.ts
1083
+ // src/config/microfrontends/utils/get-application-context.ts
1084
+ import fs5 from "node:fs";
1087
1085
  import path4 from "node:path";
1086
+ function getApplicationContext(opts) {
1087
+ if (opts?.appName) {
1088
+ return { name: opts.appName };
1089
+ }
1090
+ if (process.env.NX_TASK_TARGET_PROJECT) {
1091
+ return { name: process.env.NX_TASK_TARGET_PROJECT };
1092
+ }
1093
+ try {
1094
+ const packageJsonString = fs5.readFileSync(
1095
+ path4.join(opts?.packageRoot || ".", "package.json"),
1096
+ "utf-8"
1097
+ );
1098
+ const packageJson = JSON.parse(packageJsonString);
1099
+ if (!packageJson.name) {
1100
+ throw new MicrofrontendError(
1101
+ `package.json file missing required field "name"`,
1102
+ {
1103
+ type: "packageJson",
1104
+ subtype: "missing_field_name",
1105
+ source: "@vercel/microfrontends/next"
1106
+ }
1107
+ );
1108
+ }
1109
+ return { name: packageJson.name };
1110
+ } catch (err) {
1111
+ throw MicrofrontendError.handle(err, {
1112
+ fileName: "package.json"
1113
+ });
1114
+ }
1115
+ }
1116
+
1117
+ // src/config/microfrontends/server/utils/get-output-file-path.ts
1118
+ import path5 from "node:path";
1088
1119
 
1089
1120
  // src/config/microfrontends/server/constants.ts
1090
1121
  var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
@@ -1092,7 +1123,7 @@ var MFE_CONFIG_DEFAULT_FILE_NAME = "microfrontends.json";
1092
1123
 
1093
1124
  // src/config/microfrontends/server/utils/get-output-file-path.ts
1094
1125
  function getOutputFilePath() {
1095
- return path4.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
1126
+ return path5.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
1096
1127
  }
1097
1128
 
1098
1129
  // src/config/microfrontends/server/validation.ts
@@ -1431,8 +1462,8 @@ var MicrofrontendsServer = class extends Microfrontends {
1431
1462
  pretty: true
1432
1463
  }) {
1433
1464
  const outputPath = getOutputFilePath();
1434
- fs5.mkdirSync(dirname3(outputPath), { recursive: true });
1435
- fs5.writeFileSync(
1465
+ fs6.mkdirSync(dirname3(outputPath), { recursive: true });
1466
+ fs6.writeFileSync(
1436
1467
  outputPath,
1437
1468
  JSON.stringify(
1438
1469
  this.config.toSchemaJson(),
@@ -1518,14 +1549,8 @@ var MicrofrontendsServer = class extends Microfrontends {
1518
1549
  }
1519
1550
  try {
1520
1551
  const packageRoot = findPackageRoot(directory);
1521
- const packageJsonPath = join2(packageRoot, "package.json");
1522
- const packageJson = JSON.parse(
1523
- fs5.readFileSync(packageJsonPath, "utf-8")
1524
- );
1525
- if (!packageJson.name) {
1526
- throw new Error(`No name found in package.json at ${packageJsonPath}`);
1527
- }
1528
- const configMeta = meta ?? { fromApp: packageJson.name };
1552
+ const { name: appName } = getApplicationContext({ packageRoot });
1553
+ const configMeta = meta ?? { fromApp: appName };
1529
1554
  const maybeConfig = findConfig({ dir: packageRoot });
1530
1555
  if (maybeConfig) {
1531
1556
  return MicrofrontendsServer.fromFile({
@@ -1540,7 +1565,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1540
1565
  if (isMonorepo2) {
1541
1566
  const defaultPackage = findDefaultMicrofrontendsPackage({
1542
1567
  repositoryRoot,
1543
- applicationName: packageJson.name
1568
+ applicationName: appName
1544
1569
  });
1545
1570
  const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
1546
1571
  if (maybeConfigFromDefault) {
@@ -1570,7 +1595,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1570
1595
  options
1571
1596
  }) {
1572
1597
  try {
1573
- const configJson = fs5.readFileSync(filePath, "utf-8");
1598
+ const configJson = fs6.readFileSync(filePath, "utf-8");
1574
1599
  const config = MicrofrontendsServer.validate(configJson);
1575
1600
  if (!isMainConfig(config) && options?.resolveMainConfig) {
1576
1601
  const repositoryRoot = findRepositoryRoot();
@@ -1618,7 +1643,7 @@ var MicrofrontendsServer = class extends Microfrontends {
1618
1643
  overrides
1619
1644
  }) {
1620
1645
  try {
1621
- const config = fs5.readFileSync(filePath, "utf-8");
1646
+ const config = fs6.readFileSync(filePath, "utf-8");
1622
1647
  const validatedConfig = MicrofrontendsServer.validate(config);
1623
1648
  if (!isMainConfig(validatedConfig)) {
1624
1649
  throw new MicrofrontendError(
@@ -1649,31 +1674,18 @@ var MicrofrontendsServer = class extends Microfrontends {
1649
1674
  }
1650
1675
  };
1651
1676
 
1652
- // src/config/microfrontends/utils/get-application-context.ts
1653
- import fs6 from "node:fs";
1654
- function getApplicationContext(opts) {
1655
- if (opts?.appName) {
1656
- return { name: opts.appName };
1677
+ // src/vite/detect-framework.ts
1678
+ import { existsSync } from "node:fs";
1679
+ import { join as join2 } from "node:path";
1680
+ import { cwd } from "node:process";
1681
+ function detectFramework() {
1682
+ if (existsSync(join2(cwd(), "svelte.config.js"))) {
1683
+ return "sveltekit";
1657
1684
  }
1658
- try {
1659
- const packageJsonString = fs6.readFileSync("./package.json", "utf-8");
1660
- const packageJson = JSON.parse(packageJsonString);
1661
- if (!packageJson.name) {
1662
- throw new MicrofrontendError(
1663
- `package.json file missing required field "name"`,
1664
- {
1665
- type: "packageJson",
1666
- subtype: "missing_field_name",
1667
- source: "@vercel/microfrontends/next"
1668
- }
1669
- );
1670
- }
1671
- return { name: packageJson.name };
1672
- } catch (err) {
1673
- throw MicrofrontendError.handle(err, {
1674
- fileName: "package.json"
1675
- });
1685
+ if (existsSync(join2(cwd(), "react-router.config.js")) || existsSync(join2(cwd(), "react-router.config.ts"))) {
1686
+ return "react-router";
1676
1687
  }
1688
+ return "unknown";
1677
1689
  }
1678
1690
 
1679
1691
  // src/vite/index.ts
@@ -1685,23 +1697,45 @@ function microfrontends(opts) {
1685
1697
  }
1686
1698
  });
1687
1699
  const app = microfrontendsObj.config.getApplication(fromApp);
1700
+ if (app.isDefault() && opts?.basePath) {
1701
+ throw new Error(
1702
+ "`basePath` can not be set for the default microfrontends application."
1703
+ );
1704
+ }
1705
+ if (opts?.basePath && !opts.basePath.startsWith("/")) {
1706
+ throw new Error("`basePath` must start with a `/`");
1707
+ }
1688
1708
  const additionalConfigOptions = {};
1709
+ const framework = detectFramework();
1689
1710
  if (!app.isDefault()) {
1690
1711
  if (opts?.basePath) {
1691
- additionalConfigOptions.base = `/${opts.basePath}`;
1692
- } else {
1693
- const isSvelteKit = existsSync(join3(cwd(), "svelte.config.js"));
1694
- if (!isSvelteKit) {
1695
- additionalConfigOptions.experimental = {
1696
- renderBuiltUrl(filename, { type }) {
1697
- if (type === "asset") {
1698
- return `/${app.getAssetPrefix()}/${filename}`;
1699
- }
1700
- }
1701
- };
1712
+ if (framework !== "react-router" || !process.env.VERCEL_ENV) {
1713
+ let basePath = opts.basePath;
1714
+ if (process.env.NODE_ENV === "production" && !basePath.endsWith("/")) {
1715
+ basePath = `${basePath}/`;
1716
+ }
1717
+ additionalConfigOptions.base = basePath;
1702
1718
  }
1719
+ } else if (framework !== "sveltekit") {
1720
+ additionalConfigOptions.experimental = {
1721
+ renderBuiltUrl(filename, { type }) {
1722
+ if (type === "asset") {
1723
+ return `/${app.getAssetPrefix()}/${filename}`;
1724
+ }
1725
+ }
1726
+ };
1727
+ }
1728
+ if (framework === "react-router") {
1729
+ additionalConfigOptions.build = {
1730
+ assetsDir: `./${opts?.basePath ?? app.getAssetPrefix()}`
1731
+ };
1703
1732
  }
1704
1733
  }
1734
+ if (app.development.local.port) {
1735
+ additionalConfigOptions.server = {
1736
+ port: app.development.local.port
1737
+ };
1738
+ }
1705
1739
  return {
1706
1740
  name: "vite-plugin-vercel-microfrontends",
1707
1741
  config: () => {