@vercel/microfrontends 1.5.0 → 2.0.0-canary.0
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.
- package/CHANGELOG.md +11 -0
- package/dist/bin/cli.cjs +103 -117
- package/dist/config.cjs +48 -101
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +2 -29
- package/dist/config.js +37 -100
- package/dist/config.js.map +1 -1
- package/dist/experimental/sveltekit.cjs +93 -109
- package/dist/experimental/sveltekit.cjs.map +1 -1
- package/dist/experimental/sveltekit.js +92 -108
- package/dist/experimental/sveltekit.js.map +1 -1
- package/dist/experimental/vite.cjs +93 -109
- package/dist/experimental/vite.cjs.map +1 -1
- package/dist/experimental/vite.js +92 -108
- package/dist/experimental/vite.js.map +1 -1
- package/dist/get-application-context-e8a5a0e2.d.ts +14 -0
- package/dist/microfrontends/server.cjs +93 -109
- package/dist/microfrontends/server.cjs.map +1 -1
- package/dist/microfrontends/server.d.ts +4 -13
- package/dist/microfrontends/server.js +92 -108
- package/dist/microfrontends/server.js.map +1 -1
- package/dist/microfrontends/utils.cjs +24 -4
- package/dist/microfrontends/utils.cjs.map +1 -1
- package/dist/microfrontends/utils.d.ts +3 -1
- package/dist/microfrontends/utils.js +24 -4
- package/dist/microfrontends/utils.js.map +1 -1
- package/dist/next/client.cjs +1 -1
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/client.js.map +1 -1
- package/dist/next/config.cjs +223 -111
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js +222 -110
- package/dist/next/config.js.map +1 -1
- package/dist/next/middleware.cjs +88 -44
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.js +78 -44
- package/dist/next/middleware.js.map +1 -1
- package/dist/next/testing.cjs +51 -104
- package/dist/next/testing.cjs.map +1 -1
- package/dist/next/testing.d.ts +2 -2
- package/dist/next/testing.js +39 -102
- package/dist/next/testing.js.map +1 -1
- package/dist/overrides.d.ts +3 -3
- package/dist/schema.d.ts +2 -2
- package/dist/{types-1cec43e6.d.ts → types-0deb756b.d.ts} +16 -1
- package/dist/{types-1fb60496.d.ts → types-4299bff1.d.ts} +1 -1
- package/dist/utils/mfe-port.cjs +93 -109
- package/dist/utils/mfe-port.cjs.map +1 -1
- package/dist/utils/mfe-port.js +92 -108
- package/dist/utils/mfe-port.js.map +1 -1
- package/dist/validation.cjs +4 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +4 -0
- package/dist/validation.js.map +1 -1
- package/package.json +3 -1
- package/schema/schema.json +4 -0
|
@@ -206,8 +206,9 @@ var CONFIGURATION_FILENAMES = [
|
|
|
206
206
|
var configCache = {};
|
|
207
207
|
function findPackageWithMicrofrontendsConfig({
|
|
208
208
|
repositoryRoot,
|
|
209
|
-
|
|
209
|
+
applicationContext
|
|
210
210
|
}) {
|
|
211
|
+
const applicationName = applicationContext.name;
|
|
211
212
|
try {
|
|
212
213
|
const microfrontendsJsonPaths = fg.globSync(
|
|
213
214
|
`**/{${CONFIGURATION_FILENAMES.join(",")}}`,
|
|
@@ -249,26 +250,45 @@ ${matchingPaths.join("\n \u2022 ")}`,
|
|
|
249
250
|
);
|
|
250
251
|
}
|
|
251
252
|
if (matchingPaths.length === 0) {
|
|
253
|
+
let additionalErrorMessage = "";
|
|
254
|
+
if (microfrontendsJsonPaths.length > 0) {
|
|
255
|
+
if (!applicationContext.projectName) {
|
|
256
|
+
additionalErrorMessage = `
|
|
257
|
+
|
|
258
|
+
If the name in package.json (${applicationContext.packageJsonName}) differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`;
|
|
259
|
+
} else {
|
|
260
|
+
additionalErrorMessage = `
|
|
261
|
+
|
|
262
|
+
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
252
265
|
throw new MicrofrontendError(
|
|
253
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains "
|
|
266
|
+
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
267
|
+
|
|
268
|
+
If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable.
|
|
269
|
+
|
|
270
|
+
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
254
271
|
{ type: "config", subtype: "inference_failed" }
|
|
255
272
|
);
|
|
256
273
|
}
|
|
257
274
|
const [packageJsonPath] = matchingPaths;
|
|
258
275
|
return dirname(packageJsonPath);
|
|
259
276
|
} catch (error) {
|
|
277
|
+
if (error instanceof MicrofrontendError) {
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
260
280
|
return null;
|
|
261
281
|
}
|
|
262
282
|
}
|
|
263
283
|
function inferMicrofrontendsLocation(opts) {
|
|
264
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.
|
|
284
|
+
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}`;
|
|
265
285
|
if (configCache[cacheKey]) {
|
|
266
286
|
return configCache[cacheKey];
|
|
267
287
|
}
|
|
268
288
|
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
269
289
|
if (!result) {
|
|
270
290
|
throw new MicrofrontendError(
|
|
271
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.
|
|
291
|
+
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
272
292
|
{ type: "config", subtype: "inference_failed" }
|
|
273
293
|
);
|
|
274
294
|
}
|
|
@@ -340,90 +360,13 @@ function findConfig({ dir }) {
|
|
|
340
360
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
341
361
|
import { parse as parse2 } from "jsonc-parser";
|
|
342
362
|
|
|
343
|
-
// src/config/microfrontends-config/client/index.ts
|
|
344
|
-
import { pathToRegexp } from "path-to-regexp";
|
|
345
|
-
var regexpCache = /* @__PURE__ */ new Map();
|
|
346
|
-
var getRegexp = (path6) => {
|
|
347
|
-
const existing = regexpCache.get(path6);
|
|
348
|
-
if (existing) {
|
|
349
|
-
return existing;
|
|
350
|
-
}
|
|
351
|
-
const regexp = pathToRegexp(path6);
|
|
352
|
-
regexpCache.set(path6, regexp);
|
|
353
|
-
return regexp;
|
|
354
|
-
};
|
|
355
|
-
var MicrofrontendConfigClient = class {
|
|
356
|
-
constructor(config, opts) {
|
|
357
|
-
this.pathCache = {};
|
|
358
|
-
this.serialized = config;
|
|
359
|
-
if (opts?.removeFlaggedPaths) {
|
|
360
|
-
for (const app of Object.values(config.applications)) {
|
|
361
|
-
if (app.routing) {
|
|
362
|
-
app.routing = app.routing.filter((match) => !match.flag);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
this.applications = config.applications;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Create a new `MicrofrontendConfigClient` from a JSON string.
|
|
370
|
-
* Config must be passed in to remain framework agnostic
|
|
371
|
-
*/
|
|
372
|
-
static fromEnv(config, opts) {
|
|
373
|
-
if (!config) {
|
|
374
|
-
throw new Error(
|
|
375
|
-
"Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
return new MicrofrontendConfigClient(
|
|
379
|
-
JSON.parse(config),
|
|
380
|
-
opts
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
isEqual(other) {
|
|
384
|
-
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
385
|
-
}
|
|
386
|
-
getApplicationNameForPath(path6) {
|
|
387
|
-
if (!path6.startsWith("/")) {
|
|
388
|
-
throw new Error(`Path must start with a /`);
|
|
389
|
-
}
|
|
390
|
-
if (this.pathCache[path6]) {
|
|
391
|
-
return this.pathCache[path6];
|
|
392
|
-
}
|
|
393
|
-
const pathname = new URL(path6, "https://example.com").pathname;
|
|
394
|
-
for (const [name, application] of Object.entries(this.applications)) {
|
|
395
|
-
if (application.routing) {
|
|
396
|
-
for (const group of application.routing) {
|
|
397
|
-
for (const childPath of group.paths) {
|
|
398
|
-
const regexp = getRegexp(childPath);
|
|
399
|
-
if (regexp.test(pathname)) {
|
|
400
|
-
this.pathCache[path6] = name;
|
|
401
|
-
return name;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
const defaultApplication = Object.entries(this.applications).find(
|
|
408
|
-
([, application]) => application.default
|
|
409
|
-
);
|
|
410
|
-
if (!defaultApplication) {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
this.pathCache[path6] = defaultApplication[0];
|
|
414
|
-
return defaultApplication[0];
|
|
415
|
-
}
|
|
416
|
-
serialize() {
|
|
417
|
-
return this.serialized;
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
|
|
421
363
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
422
|
-
import { pathToRegexp
|
|
364
|
+
import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
|
|
423
365
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
424
366
|
style: "long",
|
|
425
367
|
type: "conjunction"
|
|
426
368
|
});
|
|
369
|
+
var VALID_ASSET_PREFIX_REGEXP = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
427
370
|
var validateConfigPaths = (applicationConfigsById) => {
|
|
428
371
|
if (!applicationConfigsById) {
|
|
429
372
|
return;
|
|
@@ -446,7 +389,7 @@ var validateConfigPaths = (applicationConfigsById) => {
|
|
|
446
389
|
} else {
|
|
447
390
|
pathsByApplicationId.set(path6, {
|
|
448
391
|
applications: [id],
|
|
449
|
-
matcher:
|
|
392
|
+
matcher: pathToRegexp(path6),
|
|
450
393
|
applicationId: id
|
|
451
394
|
});
|
|
452
395
|
}
|
|
@@ -550,6 +493,22 @@ var validateAppPaths = (name, app) => {
|
|
|
550
493
|
}
|
|
551
494
|
}
|
|
552
495
|
}
|
|
496
|
+
if (app.assetPrefix) {
|
|
497
|
+
if (!VALID_ASSET_PREFIX_REGEXP.test(app.assetPrefix)) {
|
|
498
|
+
throw new MicrofrontendError(
|
|
499
|
+
`Invalid asset prefix for application "${name}". ${app.assetPrefix} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`,
|
|
500
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (app.assetPrefix !== `vc-ap-${name}` && !app.routing.some(
|
|
504
|
+
(group) => group.paths.includes(`/${app.assetPrefix}/:path*`) && !group.flag
|
|
505
|
+
)) {
|
|
506
|
+
throw new MicrofrontendError(
|
|
507
|
+
`When \`assetPrefix\` is specified, \`/${app.assetPrefix}/:path*\` must be added the routing paths for the application. Changing the asset prefix is not a forwards and backwards compatible change, and the custom asset prefix should be added to \`paths\` and deployed before setting the \`assetPrefix\` field.`,
|
|
508
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
553
512
|
};
|
|
554
513
|
var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
555
514
|
if (!applicationConfigsById) {
|
|
@@ -581,6 +540,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
581
540
|
}
|
|
582
541
|
};
|
|
583
542
|
|
|
543
|
+
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
544
|
+
import md5 from "md5";
|
|
545
|
+
function hashApplicationName(name) {
|
|
546
|
+
if (!name) {
|
|
547
|
+
throw new Error("Application name is required to generate hash");
|
|
548
|
+
}
|
|
549
|
+
return md5(name).substring(0, 6).padStart(6, "0");
|
|
550
|
+
}
|
|
551
|
+
|
|
584
552
|
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
585
553
|
var PREFIX = "vc-ap";
|
|
586
554
|
function generateAssetPrefixFromName({
|
|
@@ -589,7 +557,7 @@ function generateAssetPrefixFromName({
|
|
|
589
557
|
if (!name) {
|
|
590
558
|
throw new Error("Name is required to generate an asset prefix");
|
|
591
559
|
}
|
|
592
|
-
return `${PREFIX}-${name}`;
|
|
560
|
+
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
593
561
|
}
|
|
594
562
|
|
|
595
563
|
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
@@ -749,7 +717,13 @@ var Application = class {
|
|
|
749
717
|
return this.default;
|
|
750
718
|
}
|
|
751
719
|
getAssetPrefix() {
|
|
752
|
-
|
|
720
|
+
const generatedAssetPrefix = generateAssetPrefixFromName({
|
|
721
|
+
name: this.name
|
|
722
|
+
});
|
|
723
|
+
if ("assetPrefix" in this.serialized) {
|
|
724
|
+
return this.serialized.assetPrefix ?? generatedAssetPrefix;
|
|
725
|
+
}
|
|
726
|
+
return generatedAssetPrefix;
|
|
753
727
|
}
|
|
754
728
|
getAutomationBypassEnvVarName() {
|
|
755
729
|
return generateAutomationBypassEnvVarName({ name: this.name });
|
|
@@ -883,7 +857,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
883
857
|
);
|
|
884
858
|
if (!app) {
|
|
885
859
|
throw new MicrofrontendError(
|
|
886
|
-
`Could not find microfrontends configuration for application "${name}"
|
|
860
|
+
`Could not find microfrontends configuration for application "${name}". If the name in package.json differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`,
|
|
887
861
|
{
|
|
888
862
|
type: "application",
|
|
889
863
|
subtype: "not_found"
|
|
@@ -928,23 +902,6 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
928
902
|
toSchemaJson() {
|
|
929
903
|
return this.serialized.config;
|
|
930
904
|
}
|
|
931
|
-
toClientConfig() {
|
|
932
|
-
const applications = Object.fromEntries(
|
|
933
|
-
Object.entries(this.childApplications).map(([name, application]) => [
|
|
934
|
-
name,
|
|
935
|
-
{
|
|
936
|
-
default: false,
|
|
937
|
-
routing: application.routing
|
|
938
|
-
}
|
|
939
|
-
])
|
|
940
|
-
);
|
|
941
|
-
applications[this.defaultApplication.name] = {
|
|
942
|
-
default: true
|
|
943
|
-
};
|
|
944
|
-
return new MicrofrontendConfigClient({
|
|
945
|
-
applications
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
905
|
serialize() {
|
|
949
906
|
return this.serialized;
|
|
950
907
|
}
|
|
@@ -957,8 +914,31 @@ function getApplicationContext(opts) {
|
|
|
957
914
|
if (opts?.appName) {
|
|
958
915
|
return { name: opts.appName };
|
|
959
916
|
}
|
|
917
|
+
if (process.env.VERCEL_PROJECT_NAME) {
|
|
918
|
+
return {
|
|
919
|
+
name: process.env.VERCEL_PROJECT_NAME,
|
|
920
|
+
projectName: process.env.VERCEL_PROJECT_NAME
|
|
921
|
+
};
|
|
922
|
+
}
|
|
960
923
|
if (process.env.NX_TASK_TARGET_PROJECT) {
|
|
961
|
-
return {
|
|
924
|
+
return {
|
|
925
|
+
name: process.env.NX_TASK_TARGET_PROJECT,
|
|
926
|
+
packageJsonName: process.env.NX_TASK_TARGET_PROJECT
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
try {
|
|
930
|
+
const vercelProjectJsonPath = fs5.readFileSync(
|
|
931
|
+
path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
|
|
932
|
+
"utf-8"
|
|
933
|
+
);
|
|
934
|
+
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
935
|
+
if (projectJson.projectName) {
|
|
936
|
+
return {
|
|
937
|
+
name: projectJson.projectName,
|
|
938
|
+
projectName: projectJson.projectName
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
} catch (_) {
|
|
962
942
|
}
|
|
963
943
|
try {
|
|
964
944
|
const packageJsonString = fs5.readFileSync(
|
|
@@ -976,7 +956,7 @@ function getApplicationContext(opts) {
|
|
|
976
956
|
}
|
|
977
957
|
);
|
|
978
958
|
}
|
|
979
|
-
return { name: packageJson.name };
|
|
959
|
+
return { name: packageJson.name, packageJsonName: packageJson.name };
|
|
980
960
|
} catch (err) {
|
|
981
961
|
throw MicrofrontendError.handle(err, {
|
|
982
962
|
fileName: "package.json"
|
|
@@ -1116,6 +1096,10 @@ var schema_default = {
|
|
|
1116
1096
|
routing: {
|
|
1117
1097
|
$ref: "#/definitions/Routing",
|
|
1118
1098
|
description: "Groups of path expressions that are routed to this application."
|
|
1099
|
+
},
|
|
1100
|
+
assetPrefix: {
|
|
1101
|
+
type: "string",
|
|
1102
|
+
description: "The name of the asset prefix to use instead of the auto-generated name.\n\nThe asset prefix is used to prefix all paths to static assets, such as JS, CSS, or images that are served by a specific application. It is necessary to ensure there are no conflicts with other applications on the same domain.\n\nAn auto-generated asset prefix of the form `vc-ap-<hash>` is used when this field is not provided.\n\nWhen this field is provided, `/${assetPrefix}/:path*` must also be added to the list of paths in the `routing` field. Changing the asset prefix after a microfrontend application has already been deployed is not a forwards and backwards compatible change, and the asset prefix should be added to the `routing` field and deployed before setting the `assetPrefix` field."
|
|
1119
1103
|
}
|
|
1120
1104
|
},
|
|
1121
1105
|
required: [
|
|
@@ -1341,7 +1325,7 @@ var MicrofrontendsServer = class {
|
|
|
1341
1325
|
}
|
|
1342
1326
|
try {
|
|
1343
1327
|
const packageRoot = findPackageRoot(directory);
|
|
1344
|
-
const
|
|
1328
|
+
const applicationContext = getApplicationContext({ packageRoot });
|
|
1345
1329
|
const maybeConfig = findConfig({ dir: packageRoot });
|
|
1346
1330
|
if (maybeConfig) {
|
|
1347
1331
|
return MicrofrontendsServer.fromFile({
|
|
@@ -1375,7 +1359,7 @@ var MicrofrontendsServer = class {
|
|
|
1375
1359
|
if (isMonorepo2) {
|
|
1376
1360
|
const defaultPackage = inferMicrofrontendsLocation({
|
|
1377
1361
|
repositoryRoot,
|
|
1378
|
-
|
|
1362
|
+
applicationContext
|
|
1379
1363
|
});
|
|
1380
1364
|
const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
|
|
1381
1365
|
if (maybeConfigFromDefault) {
|