@vercel/microfrontends 2.0.0-canary.2 → 2.0.1

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/CHANGELOG.md +16 -11
  2. package/dist/bin/cli.cjs +180 -63
  3. package/dist/config.cjs +5 -4
  4. package/dist/config.cjs.map +1 -1
  5. package/dist/config.js +5 -4
  6. package/dist/config.js.map +1 -1
  7. package/dist/experimental/sveltekit.cjs +5 -4
  8. package/dist/experimental/sveltekit.cjs.map +1 -1
  9. package/dist/experimental/sveltekit.js +5 -4
  10. package/dist/experimental/sveltekit.js.map +1 -1
  11. package/dist/experimental/vite.cjs +5 -4
  12. package/dist/experimental/vite.cjs.map +1 -1
  13. package/dist/experimental/vite.js +5 -4
  14. package/dist/experimental/vite.js.map +1 -1
  15. package/dist/microfrontends/server.cjs +5 -4
  16. package/dist/microfrontends/server.cjs.map +1 -1
  17. package/dist/microfrontends/server.js +5 -4
  18. package/dist/microfrontends/server.js.map +1 -1
  19. package/dist/next/client.cjs +1 -1
  20. package/dist/next/client.cjs.map +1 -1
  21. package/dist/next/client.d.ts +15 -1
  22. package/dist/next/client.js +1 -1
  23. package/dist/next/client.js.map +1 -1
  24. package/dist/next/config.cjs +5 -4
  25. package/dist/next/config.cjs.map +1 -1
  26. package/dist/next/config.js +5 -4
  27. package/dist/next/config.js.map +1 -1
  28. package/dist/next/middleware.cjs +5 -4
  29. package/dist/next/middleware.cjs.map +1 -1
  30. package/dist/next/middleware.js +5 -4
  31. package/dist/next/middleware.js.map +1 -1
  32. package/dist/next/testing.cjs +5 -4
  33. package/dist/next/testing.cjs.map +1 -1
  34. package/dist/next/testing.js +5 -4
  35. package/dist/next/testing.js.map +1 -1
  36. package/dist/utils/mfe-port.cjs +20 -24
  37. package/dist/utils/mfe-port.cjs.map +1 -1
  38. package/dist/utils/mfe-port.js +20 -24
  39. package/dist/utils/mfe-port.js.map +1 -1
  40. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,28 +1,33 @@
1
1
  # @vercel/microfrontends
2
2
 
3
- ## 2.0.0-canary.2
3
+ ## 2.0.1
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Use user-provided appName when inferring the location of microfrontends.json.
8
-
9
- ## 2.0.0-canary.1
10
-
11
- ### Patch Changes
12
-
13
- - Support old and new asset prefixes in local development proxy.
7
+ - 8c11bdc:
8
+ - Support hyphens and escaped special characters in supported path matching regex https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions
9
+ - Improve error message for when local development proxy can't determine the port
10
+ - Update local proxy double slash routing behaviour to match the production proxy
14
11
 
15
- ## 2.0.0-canary.0
12
+ ## 2.0.0
16
13
 
17
- > **Check out our [Public Beta](https://vercel.com/changelog/microfrontends-are-now-in-public-beta) changelog to learn more about this release.**
14
+ > **Check out our [Public Beta](https://vercel.com/changelog/microfrontends-support-is-now-in-public-beta) changelog to learn more about this release.**
18
15
 
19
16
  ### Major Changes
20
17
 
21
18
  - This release removes the project name and flag names from being visible to client side code.
22
19
  - Modify the auto-generated asset prefix to use a hash of the project name instead of the project name itself.
23
- - Allow users to specify a custom asset prefix in the `microfrontends.json`
20
+ - Allow users to specify a custom asset prefix in `microfrontends.json`.
24
21
  - Remove project names and flag names from the Microfrontends client configuration.
25
22
 
23
+ ### Minor Changes
24
+
25
+ - Use user-provided appName when inferring the location of microfrontends.json.
26
+
27
+ ### Patch Changes
28
+
29
+ - Improve error messages when the name in `package.json` does not match the Vercel project name.
30
+
26
31
  ## 1.5.0
27
32
 
28
33
  ### Minor Changes
package/dist/bin/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var import_env = require("@next/env");
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@vercel/microfrontends",
33
- version: "2.0.0-canary.2",
33
+ version: "2.0.1",
34
34
  private: false,
35
35
  description: "Defines configuration and utilities for microfrontends development",
36
36
  keywords: [
@@ -169,11 +169,11 @@ var package_default = {
169
169
  typecheck: "tsc --noEmit"
170
170
  },
171
171
  dependencies: {
172
- "@next/env": "15.4.0-canary.41",
172
+ "@next/env": "15.5.4",
173
173
  "@types/md5": "^2.3.5",
174
174
  ajv: "^8.17.1",
175
175
  commander: "^12.1.0",
176
- cookie: "0.4.0",
176
+ cookie: "1.0.2",
177
177
  "fast-glob": "^3.3.2",
178
178
  "http-proxy": "^1.18.1",
179
179
  "jsonc-parser": "^3.3.1",
@@ -198,7 +198,7 @@ var package_default = {
198
198
  "eslint-config-custom": "workspace:*",
199
199
  jest: "^29.7.0",
200
200
  "jest-environment-jsdom": "29.2.2",
201
- next: "15.4.0-canary.41",
201
+ next: "15.5.4",
202
202
  react: "19.0.0",
203
203
  "react-dom": "19.0.0",
204
204
  "ts-config": "workspace:*",
@@ -452,7 +452,7 @@ var MicrofrontendConfigClient = class {
452
452
  static fromEnv(config) {
453
453
  if (!config) {
454
454
  throw new Error(
455
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
455
+ "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
456
456
  );
457
457
  }
458
458
  return new MicrofrontendConfigClient(JSON.parse(config));
@@ -592,10 +592,11 @@ function validatePathExpression(path7) {
592
592
  }
593
593
  if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
594
594
  // Only limited regex is supported for now, due to performance considerations
595
- !/^(?<allowed>[\w]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^:|()]+)*)\)\.\*$/.test(
596
- token.pattern
595
+ // Allows all letters, numbers, and hyphens. Other characters must be escaped.
596
+ !/^(?<allowed>[\w-~]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w-~]+(?:\|[^:|()]+)*)\)\.\*$/.test(
597
+ token.pattern.replace(/\\./g, "")
597
598
  )) {
598
- return `Path ${path7} cannot use unsupported regular expression wildcard`;
599
+ return `Path ${path7} cannot use unsupported regular expression wildcard. If the path includes special characters, they must be escaped with backslash (e.g. '\\(')`;
599
600
  }
600
601
  if (token.modifier && i !== tokens.length - 1) {
601
602
  return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path7}. Modifiers are only allowed in the last path component`;
@@ -2112,12 +2113,6 @@ var localAuthHtml = ({
2112
2113
  var MFE_LOCAL_PROXY_HEADER = "x-vercel-mfe-local-proxy-origin";
2113
2114
  var MFE_FLAG_VALUE = "vercel-mfe-flag-value";
2114
2115
  var MFE_FLAG_VALUE_HEADER = `x-${MFE_FLAG_VALUE}`;
2115
- var MFE_DEBUG = process.env.MFE_DEBUG;
2116
- var mfeDebug = (message) => {
2117
- if (MFE_DEBUG === "true" || MFE_DEBUG === "1") {
2118
- console.log(message);
2119
- }
2120
- };
2121
2116
  var ProxyRequestRouter = class {
2122
2117
  constructor(config, {
2123
2118
  localApps
@@ -2130,8 +2125,10 @@ var ProxyRequestRouter = class {
2130
2125
  return this.getApplicationTarget(defaultApp);
2131
2126
  }
2132
2127
  getApplicationTarget(application) {
2133
- const useDev = this.localApps.find(
2134
- (name) => name === application.name || name === application.packageName
2128
+ const useDev = Boolean(
2129
+ this.localApps.find(
2130
+ (name) => name === application.name || name === application.packageName
2131
+ )
2135
2132
  );
2136
2133
  let applicationName = application.name;
2137
2134
  let host = useDev ? application.development.local : application.fallback;
@@ -2151,7 +2148,9 @@ var ProxyRequestRouter = class {
2151
2148
  protocol,
2152
2149
  hostname,
2153
2150
  port,
2154
- application: applicationName
2151
+ application: applicationName,
2152
+ isLocal: useDev,
2153
+ originalApplication: application.name
2155
2154
  };
2156
2155
  }
2157
2156
  /**
@@ -2225,8 +2224,8 @@ var ProxyRequestRouter = class {
2225
2224
  if (target)
2226
2225
  return target;
2227
2226
  const defaultHost = this.getDefaultHost(config);
2228
- mfeDebug(
2229
- `no matching routes, routing ${path7} to default application: ${JSON.stringify(defaultHost)}`
2227
+ console.log(
2228
+ ` ${path7} - Did not match any routes. Routing to default app: ${formatProxyTarget(defaultHost)}`
2230
2229
  );
2231
2230
  return { path: path7, ...defaultHost };
2232
2231
  }
@@ -2242,8 +2241,8 @@ var ProxyRequestRouter = class {
2242
2241
  const target = this.getApplicationTarget(application);
2243
2242
  if (middlewareMfeZone) {
2244
2243
  if (middlewareMfeZone === application.name) {
2245
- mfeDebug(
2246
- `routing ${path7} to '${target.application}' at ${target.hostname} according to 'x-vercel-mfe-zone' header`
2244
+ console.log(
2245
+ ` ${path7} - Routing to ${formatProxyTarget(target)} according to 'x-vercel-mfe-zone' header`
2247
2246
  );
2248
2247
  return { path: path7, ...target };
2249
2248
  }
@@ -2262,15 +2261,12 @@ var ProxyRequestRouter = class {
2262
2261
  for (const childPath of group.paths) {
2263
2262
  const regexp = (0, import_path_to_regexp3.pathToRegexp)(childPath);
2264
2263
  if (regexp.test(url.pathname)) {
2265
- mfeDebug(
2266
- `routing ${path7} to '${target.application}' at ${target.hostname}`
2267
- );
2268
2264
  if (group.flag) {
2269
2265
  if (mfeFlagValue === true) {
2270
2266
  } else if (mfeFlagValue === false) {
2271
2267
  continue;
2272
2268
  } else {
2273
- mfeDebug(
2269
+ console.log(
2274
2270
  "Routing group is behind flag. Routing to default app to check flag via middleware."
2275
2271
  );
2276
2272
  if (!this.isDefaultAppLocal()) {
@@ -2282,6 +2278,9 @@ var ProxyRequestRouter = class {
2282
2278
  return null;
2283
2279
  }
2284
2280
  }
2281
+ console.log(
2282
+ ` ${path7} - Matched ${childPath}. Routing to ${formatProxyTarget(target)}`
2283
+ );
2285
2284
  return { path: path7, ...target };
2286
2285
  }
2287
2286
  }
@@ -2310,8 +2309,8 @@ var ProxyRequestRouter = class {
2310
2309
  for (const rewrite of rewrites) {
2311
2310
  for (const assetPrefix of assetPrefixes) {
2312
2311
  if ((0, import_path_to_regexp3.pathToRegexp)(`/${assetPrefix}${rewrite}`).test(pathname)) {
2313
- mfeDebug(
2314
- `routing ${pathname} to '${target.application}' at ${target.hostname}`
2312
+ console.log(
2313
+ ` ${pathname} - Matched asset prefix. Routing to ${formatProxyTarget(target)}`
2315
2314
  );
2316
2315
  return {
2317
2316
  path: path7,
@@ -2339,13 +2338,16 @@ var ProxyRequestRouter = class {
2339
2338
  url: refererURL,
2340
2339
  applications
2341
2340
  });
2342
- mfeDebug(
2343
- `routing nextjs stack frame request to ${refererApp?.application}`
2341
+ if (!refererApp) {
2342
+ return null;
2343
+ }
2344
+ console.log(
2345
+ ` ${refererURL.pathname} - Routing nextjs stack frame request to ${formatProxyTarget(refererApp)}`
2344
2346
  );
2345
- return refererApp ? {
2347
+ return {
2346
2348
  ...refererApp,
2347
2349
  path: `${url.pathname}${url.search}`
2348
- } : null;
2350
+ };
2349
2351
  }
2350
2352
  checkNextSourceMap({ url }) {
2351
2353
  const isSourceMap = (0, import_path_to_regexp3.pathToRegexp)("/__nextjs_source-map").test(url.pathname);
@@ -2354,11 +2356,15 @@ var ProxyRequestRouter = class {
2354
2356
  }
2355
2357
  const localApp = this.getArbitraryLocalApp();
2356
2358
  if (!localApp) {
2357
- mfeDebug("no locally running application to route request to");
2359
+ console.error(
2360
+ ` ${url.pathname} - No locally running application to route request to`
2361
+ );
2358
2362
  return null;
2359
2363
  }
2360
2364
  const target = this.getApplicationTarget(localApp);
2361
- mfeDebug(`routing nextjs source map request to ${target.application}`);
2365
+ console.log(
2366
+ ` ${url.pathname} - Routing nextjs source map request to randomly selected local application: ${formatProxyTarget(target)}`
2367
+ );
2362
2368
  return {
2363
2369
  ...target,
2364
2370
  path: `${url.pathname}${url.search}`
@@ -2374,7 +2380,9 @@ var ProxyRequestRouter = class {
2374
2380
  }
2375
2381
  const imageUrl = url.searchParams.get("url");
2376
2382
  if (!imageUrl) {
2377
- mfeDebug("no url parameter found in _next/image request");
2383
+ console.error(
2384
+ ` ${url.pathname}?${url.search} - No url parameter found in _next/image request`
2385
+ );
2378
2386
  return null;
2379
2387
  }
2380
2388
  const decodedPath = decodeURIComponent(imageUrl);
@@ -2384,11 +2392,16 @@ var ProxyRequestRouter = class {
2384
2392
  url: imageURL,
2385
2393
  applications
2386
2394
  });
2387
- mfeDebug(`routing nextjs image request to ${imageApp?.application}`);
2388
- return imageApp ? {
2395
+ if (!imageApp) {
2396
+ return null;
2397
+ }
2398
+ console.log(
2399
+ ` ${url.pathname}?${url.search} - Routing nextjs image request to ${formatProxyTarget(imageApp)}`
2400
+ );
2401
+ return {
2389
2402
  ...imageApp,
2390
2403
  path: `${url.pathname}${url.search}`
2391
- } : null;
2404
+ };
2392
2405
  }
2393
2406
  isDefaultAppLocal() {
2394
2407
  const defaultApp = this.config.getDefaultApplication();
@@ -2410,10 +2423,12 @@ var ProxyRequestRouter = class {
2410
2423
  var LocalProxy = class {
2411
2424
  constructor(config, {
2412
2425
  localApps,
2413
- proxyPort
2426
+ proxyPort,
2427
+ configFilePath
2414
2428
  }) {
2415
2429
  this.router = new ProxyRequestRouter(config, { localApps });
2416
2430
  this.proxyPort = proxyPort ?? this.router.config.getLocalProxyPort();
2431
+ this.configFilePath = configFilePath;
2417
2432
  this.proxy = import_http_proxy.default.createProxyServer({ secure: true });
2418
2433
  this.proxy.on("error", (err, req, res) => {
2419
2434
  if (res instanceof http.ServerResponse) {
@@ -2423,9 +2438,12 @@ var LocalProxy = class {
2423
2438
  }
2424
2439
  const target = this.router.getTarget(req);
2425
2440
  res.end(
2426
- `Error proxying request to ${target.application}. Is the server running locally on port ${target.port}?`
2441
+ `Error proxying request to ${formatProxyTarget(target)}. Is the server running locally on port ${target.port}?`
2442
+ );
2443
+ console.error(
2444
+ `Error proxying request for ${formatProxyTarget(target)}: `,
2445
+ err
2427
2446
  );
2428
- console.error(`Error proxying request for ${target.application}: `, err);
2429
2447
  });
2430
2448
  }
2431
2449
  static fromFile(filePath, {
@@ -2441,7 +2459,11 @@ var LocalProxy = class {
2441
2459
  microfrontends = MicrofrontendsServer.infer();
2442
2460
  }
2443
2461
  LocalProxy.validateLocalApps(localApps, microfrontends.config);
2444
- return new LocalProxy(microfrontends.config, { localApps, proxyPort });
2462
+ return new LocalProxy(microfrontends.config, {
2463
+ localApps,
2464
+ proxyPort,
2465
+ configFilePath: filePath
2466
+ });
2445
2467
  }
2446
2468
  static validateLocalApps(localApps, config) {
2447
2469
  const unknownApps = [];
@@ -2478,13 +2500,37 @@ var LocalProxy = class {
2478
2500
  }
2479
2501
  });
2480
2502
  httpServer.listen(this.proxyPort, () => {
2481
- console.log(`Microfrontends Proxy: http://localhost:${this.proxyPort}`);
2503
+ this.displayStartupMessage();
2482
2504
  });
2483
2505
  }
2484
2506
  handleRequest(req, res) {
2485
2507
  if (this.handleProxyInfoRequest(req.url, res)) {
2486
2508
  return;
2487
2509
  }
2510
+ if (req.url?.includes("//")) {
2511
+ const originalUrl = req.url;
2512
+ if (originalUrl) {
2513
+ const normalizedUrl = originalUrl.replaceAll(/\/[\\/]+/g, "/");
2514
+ if (normalizedUrl !== originalUrl) {
2515
+ res.writeHead(307, {
2516
+ Location: normalizedUrl,
2517
+ // Copy incoming request headers except hop-by-hop headers and Location
2518
+ ...Object.fromEntries(
2519
+ Object.entries(req.headers).filter(
2520
+ ([key]) => ![
2521
+ "connection",
2522
+ "content-length",
2523
+ "transfer-encoding",
2524
+ "location"
2525
+ ].includes(key.toLowerCase())
2526
+ )
2527
+ )
2528
+ });
2529
+ res.end();
2530
+ return;
2531
+ }
2532
+ }
2533
+ }
2488
2534
  const target = this.router.getTarget(req);
2489
2535
  const { req: strippedReq, mfeFlagValue } = removeMfeFlagQuery(req);
2490
2536
  if (target.protocol === "https") {
@@ -2518,7 +2564,7 @@ var LocalProxy = class {
2518
2564
  })
2519
2565
  );
2520
2566
  }
2521
- if (realRes.statusCode === 307) {
2567
+ if (realRes.statusCode === 307 || realRes.statusCode === 308 || realRes.statusCode === 302 || realRes.statusCode === 301) {
2522
2568
  const locationHeader = realRes.headers.location;
2523
2569
  if (locationHeader) {
2524
2570
  const redirectUrl = new import_node_url.URL(
@@ -2579,6 +2625,79 @@ var LocalProxy = class {
2579
2625
  }
2580
2626
  return false;
2581
2627
  }
2628
+ displayStartupMessage() {
2629
+ const allApps = this.router.config.getAllApplications();
2630
+ const localApps = [];
2631
+ const fallbackApps = [];
2632
+ const defaultApp = this.router.config.getDefaultApplication();
2633
+ const defaultFallback = defaultApp.fallback.host;
2634
+ for (const app of allApps) {
2635
+ const isLocal = this.router.localApps.find(
2636
+ (name) => name === app.name || name === app.packageName
2637
+ );
2638
+ if (isLocal) {
2639
+ localApps.push({
2640
+ name: app.name,
2641
+ port: app.development.local.port
2642
+ });
2643
+ } else {
2644
+ const target = this.router.getApplicationTarget(app);
2645
+ let fallbackHost = target.hostname;
2646
+ if (!app.fallback) {
2647
+ fallbackHost = defaultFallback;
2648
+ }
2649
+ fallbackApps.push({
2650
+ name: app.name,
2651
+ fallback: fallbackHost
2652
+ });
2653
+ }
2654
+ }
2655
+ console.log(`
2656
+ \u25B2 Microfrontends Proxy (${package_default.version}) Started`);
2657
+ console.log(` - Proxy URL: http://localhost:${this.proxyPort}`);
2658
+ if (this.configFilePath) {
2659
+ console.log(` - Config: ${this.configFilePath}`);
2660
+ }
2661
+ if (localApps.length > 0) {
2662
+ console.log(" - Local Applications:");
2663
+ const displayLocalApps = localApps.length > 5 ? [
2664
+ ...localApps.slice(0, 5),
2665
+ { name: `... and ${localApps.length - 5} more`, port: void 0 }
2666
+ ] : localApps;
2667
+ for (const app of displayLocalApps) {
2668
+ if (app.port !== void 0) {
2669
+ console.log(` \u2022 ${app.name} (port ${app.port})`);
2670
+ } else {
2671
+ console.log(` \u2022 ${app.name}`);
2672
+ }
2673
+ }
2674
+ }
2675
+ if (fallbackApps.length > 0) {
2676
+ console.log(" - Fallback Applications:");
2677
+ const displayFallbackApps = fallbackApps.length > 5 ? [
2678
+ ...fallbackApps.slice(0, 5),
2679
+ { name: `... and ${fallbackApps.length - 5} more`, fallback: "" }
2680
+ ] : fallbackApps;
2681
+ for (const app of displayFallbackApps) {
2682
+ if (app.fallback) {
2683
+ console.log(` \u2022 ${app.name} \u2192 ${app.fallback}`);
2684
+ } else {
2685
+ console.log(` \u2022 ${app.name}`);
2686
+ }
2687
+ }
2688
+ }
2689
+ if (localApps.length === 0 && fallbackApps.length === 0) {
2690
+ console.log(" - No applications configured\n");
2691
+ }
2692
+ if (localApps.length > 0) {
2693
+ console.log(
2694
+ "\nRequests directly to the ports of these applications may be automatically\nredirected to this proxy. Set the MFE_DISABLE_LOCAL_PROXY_REWRITE=1\nenvironment variable to disable this behavior."
2695
+ );
2696
+ }
2697
+ console.log(`
2698
+ ${"\u2500".repeat(50)}
2699
+ `);
2700
+ }
2582
2701
  };
2583
2702
  function extractMfeFlagValue(path7) {
2584
2703
  const host = "http://example.com";
@@ -2608,6 +2727,9 @@ function removeMfeFlagQuery(req) {
2608
2727
  req.url = path7;
2609
2728
  return { req, mfeFlagValue };
2610
2729
  }
2730
+ function formatProxyTarget(target) {
2731
+ return `${target.originalApplication} (${target.isLocal ? "local" : "fallback"})`;
2732
+ }
2611
2733
 
2612
2734
  // src/bin/port.ts
2613
2735
  var import_node_process = require("process");
@@ -2617,19 +2739,19 @@ var import_node_path9 = __toESM(require("path"), 1);
2617
2739
  var import_node_fs8 = __toESM(require("fs"), 1);
2618
2740
  function mfePort(packageDir) {
2619
2741
  const { name: appName, version } = getPackageJson(packageDir);
2620
- const result = loadConfig({ packageDir, appName });
2621
- if (!result) {
2622
- throw new MicrofrontendError(
2623
- `Unable to determine configured port for ${appName}`,
2624
- { type: "config", subtype: "not_found" }
2625
- );
2742
+ try {
2743
+ const result = loadConfig({ packageDir, appName });
2744
+ const { port } = result;
2745
+ return {
2746
+ name: appName,
2747
+ version,
2748
+ port
2749
+ };
2750
+ } catch (e) {
2751
+ throw new Error(`Unable to determine configured port for ${appName}`, {
2752
+ cause: e
2753
+ });
2626
2754
  }
2627
- const { port } = result;
2628
- return {
2629
- name: appName,
2630
- version,
2631
- port
2632
- };
2633
2755
  }
2634
2756
  function getPackageJson(packageDir) {
2635
2757
  const filePath = import_node_path9.default.join(packageDir, "package.json");
@@ -2639,14 +2761,9 @@ function loadConfig({
2639
2761
  packageDir,
2640
2762
  appName
2641
2763
  }) {
2642
- let config;
2643
- try {
2644
- config = MicrofrontendsServer.infer({
2645
- directory: packageDir
2646
- });
2647
- } catch (e) {
2648
- return void 0;
2649
- }
2764
+ const config = MicrofrontendsServer.infer({
2765
+ directory: packageDir
2766
+ });
2650
2767
  const app = config.config.getApplication(appName);
2651
2768
  const port = app.development.local.port ?? (app.development.local.protocol === "https" ? 443 : 80);
2652
2769
  return { port };
package/dist/config.cjs CHANGED
@@ -232,7 +232,7 @@ var MicrofrontendConfigClient = class {
232
232
  static fromEnv(config) {
233
233
  if (!config) {
234
234
  throw new Error(
235
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
235
+ "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
236
236
  );
237
237
  }
238
238
  return new MicrofrontendConfigClient(JSON.parse(config));
@@ -372,10 +372,11 @@ function validatePathExpression(path) {
372
372
  }
373
373
  if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
374
374
  // Only limited regex is supported for now, due to performance considerations
375
- !/^(?<allowed>[\w]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^:|()]+)*)\)\.\*$/.test(
376
- token.pattern
375
+ // Allows all letters, numbers, and hyphens. Other characters must be escaped.
376
+ !/^(?<allowed>[\w-~]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w-~]+(?:\|[^:|()]+)*)\)\.\*$/.test(
377
+ token.pattern.replace(/\\./g, "")
377
378
  )) {
378
- return `Path ${path} cannot use unsupported regular expression wildcard`;
379
+ return `Path ${path} cannot use unsupported regular expression wildcard. If the path includes special characters, they must be escaped with backslash (e.g. '\\(')`;
379
380
  }
380
381
  if (token.modifier && i !== tokens.length - 1) {
381
382
  return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path}. Modifiers are only allowed in the last path component`;