@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
package/dist/utils/mfe-port.js
CHANGED
|
@@ -201,8 +201,9 @@ var CONFIGURATION_FILENAMES = [
|
|
|
201
201
|
var configCache = {};
|
|
202
202
|
function findPackageWithMicrofrontendsConfig({
|
|
203
203
|
repositoryRoot,
|
|
204
|
-
|
|
204
|
+
applicationContext
|
|
205
205
|
}) {
|
|
206
|
+
const applicationName = applicationContext.name;
|
|
206
207
|
try {
|
|
207
208
|
const microfrontendsJsonPaths = fg.globSync(
|
|
208
209
|
`**/{${CONFIGURATION_FILENAMES.join(",")}}`,
|
|
@@ -244,26 +245,45 @@ ${matchingPaths.join("\n \u2022 ")}`,
|
|
|
244
245
|
);
|
|
245
246
|
}
|
|
246
247
|
if (matchingPaths.length === 0) {
|
|
248
|
+
let additionalErrorMessage = "";
|
|
249
|
+
if (microfrontendsJsonPaths.length > 0) {
|
|
250
|
+
if (!applicationContext.projectName) {
|
|
251
|
+
additionalErrorMessage = `
|
|
252
|
+
|
|
253
|
+
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.`;
|
|
254
|
+
} else {
|
|
255
|
+
additionalErrorMessage = `
|
|
256
|
+
|
|
257
|
+
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
247
260
|
throw new MicrofrontendError(
|
|
248
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains "
|
|
261
|
+
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
262
|
+
|
|
263
|
+
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.
|
|
264
|
+
|
|
265
|
+
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
249
266
|
{ type: "config", subtype: "inference_failed" }
|
|
250
267
|
);
|
|
251
268
|
}
|
|
252
269
|
const [packageJsonPath] = matchingPaths;
|
|
253
270
|
return dirname(packageJsonPath);
|
|
254
271
|
} catch (error) {
|
|
272
|
+
if (error instanceof MicrofrontendError) {
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
255
275
|
return null;
|
|
256
276
|
}
|
|
257
277
|
}
|
|
258
278
|
function inferMicrofrontendsLocation(opts) {
|
|
259
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.
|
|
279
|
+
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}`;
|
|
260
280
|
if (configCache[cacheKey]) {
|
|
261
281
|
return configCache[cacheKey];
|
|
262
282
|
}
|
|
263
283
|
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
264
284
|
if (!result) {
|
|
265
285
|
throw new MicrofrontendError(
|
|
266
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.
|
|
286
|
+
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
267
287
|
{ type: "config", subtype: "inference_failed" }
|
|
268
288
|
);
|
|
269
289
|
}
|
|
@@ -335,90 +355,13 @@ function findConfig({ dir }) {
|
|
|
335
355
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
336
356
|
import { parse as parse2 } from "jsonc-parser";
|
|
337
357
|
|
|
338
|
-
// src/config/microfrontends-config/client/index.ts
|
|
339
|
-
import { pathToRegexp } from "path-to-regexp";
|
|
340
|
-
var regexpCache = /* @__PURE__ */ new Map();
|
|
341
|
-
var getRegexp = (path7) => {
|
|
342
|
-
const existing = regexpCache.get(path7);
|
|
343
|
-
if (existing) {
|
|
344
|
-
return existing;
|
|
345
|
-
}
|
|
346
|
-
const regexp = pathToRegexp(path7);
|
|
347
|
-
regexpCache.set(path7, regexp);
|
|
348
|
-
return regexp;
|
|
349
|
-
};
|
|
350
|
-
var MicrofrontendConfigClient = class {
|
|
351
|
-
constructor(config, opts) {
|
|
352
|
-
this.pathCache = {};
|
|
353
|
-
this.serialized = config;
|
|
354
|
-
if (opts?.removeFlaggedPaths) {
|
|
355
|
-
for (const app of Object.values(config.applications)) {
|
|
356
|
-
if (app.routing) {
|
|
357
|
-
app.routing = app.routing.filter((match) => !match.flag);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
this.applications = config.applications;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Create a new `MicrofrontendConfigClient` from a JSON string.
|
|
365
|
-
* Config must be passed in to remain framework agnostic
|
|
366
|
-
*/
|
|
367
|
-
static fromEnv(config, opts) {
|
|
368
|
-
if (!config) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
"Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
return new MicrofrontendConfigClient(
|
|
374
|
-
JSON.parse(config),
|
|
375
|
-
opts
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
isEqual(other) {
|
|
379
|
-
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
380
|
-
}
|
|
381
|
-
getApplicationNameForPath(path7) {
|
|
382
|
-
if (!path7.startsWith("/")) {
|
|
383
|
-
throw new Error(`Path must start with a /`);
|
|
384
|
-
}
|
|
385
|
-
if (this.pathCache[path7]) {
|
|
386
|
-
return this.pathCache[path7];
|
|
387
|
-
}
|
|
388
|
-
const pathname = new URL(path7, "https://example.com").pathname;
|
|
389
|
-
for (const [name, application] of Object.entries(this.applications)) {
|
|
390
|
-
if (application.routing) {
|
|
391
|
-
for (const group of application.routing) {
|
|
392
|
-
for (const childPath of group.paths) {
|
|
393
|
-
const regexp = getRegexp(childPath);
|
|
394
|
-
if (regexp.test(pathname)) {
|
|
395
|
-
this.pathCache[path7] = name;
|
|
396
|
-
return name;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
const defaultApplication = Object.entries(this.applications).find(
|
|
403
|
-
([, application]) => application.default
|
|
404
|
-
);
|
|
405
|
-
if (!defaultApplication) {
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
this.pathCache[path7] = defaultApplication[0];
|
|
409
|
-
return defaultApplication[0];
|
|
410
|
-
}
|
|
411
|
-
serialize() {
|
|
412
|
-
return this.serialized;
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
|
|
416
358
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
417
|
-
import { pathToRegexp
|
|
359
|
+
import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
|
|
418
360
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
419
361
|
style: "long",
|
|
420
362
|
type: "conjunction"
|
|
421
363
|
});
|
|
364
|
+
var VALID_ASSET_PREFIX_REGEXP = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
422
365
|
var validateConfigPaths = (applicationConfigsById) => {
|
|
423
366
|
if (!applicationConfigsById) {
|
|
424
367
|
return;
|
|
@@ -441,7 +384,7 @@ var validateConfigPaths = (applicationConfigsById) => {
|
|
|
441
384
|
} else {
|
|
442
385
|
pathsByApplicationId.set(path7, {
|
|
443
386
|
applications: [id],
|
|
444
|
-
matcher:
|
|
387
|
+
matcher: pathToRegexp(path7),
|
|
445
388
|
applicationId: id
|
|
446
389
|
});
|
|
447
390
|
}
|
|
@@ -545,6 +488,22 @@ var validateAppPaths = (name, app) => {
|
|
|
545
488
|
}
|
|
546
489
|
}
|
|
547
490
|
}
|
|
491
|
+
if (app.assetPrefix) {
|
|
492
|
+
if (!VALID_ASSET_PREFIX_REGEXP.test(app.assetPrefix)) {
|
|
493
|
+
throw new MicrofrontendError(
|
|
494
|
+
`Invalid asset prefix for application "${name}". ${app.assetPrefix} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`,
|
|
495
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
if (app.assetPrefix !== `vc-ap-${name}` && !app.routing.some(
|
|
499
|
+
(group) => group.paths.includes(`/${app.assetPrefix}/:path*`) && !group.flag
|
|
500
|
+
)) {
|
|
501
|
+
throw new MicrofrontendError(
|
|
502
|
+
`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.`,
|
|
503
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
548
507
|
};
|
|
549
508
|
var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
550
509
|
if (!applicationConfigsById) {
|
|
@@ -576,6 +535,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
576
535
|
}
|
|
577
536
|
};
|
|
578
537
|
|
|
538
|
+
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
539
|
+
import md5 from "md5";
|
|
540
|
+
function hashApplicationName(name) {
|
|
541
|
+
if (!name) {
|
|
542
|
+
throw new Error("Application name is required to generate hash");
|
|
543
|
+
}
|
|
544
|
+
return md5(name).substring(0, 6).padStart(6, "0");
|
|
545
|
+
}
|
|
546
|
+
|
|
579
547
|
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
580
548
|
var PREFIX = "vc-ap";
|
|
581
549
|
function generateAssetPrefixFromName({
|
|
@@ -584,7 +552,7 @@ function generateAssetPrefixFromName({
|
|
|
584
552
|
if (!name) {
|
|
585
553
|
throw new Error("Name is required to generate an asset prefix");
|
|
586
554
|
}
|
|
587
|
-
return `${PREFIX}-${name}`;
|
|
555
|
+
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
588
556
|
}
|
|
589
557
|
|
|
590
558
|
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
@@ -744,7 +712,13 @@ var Application = class {
|
|
|
744
712
|
return this.default;
|
|
745
713
|
}
|
|
746
714
|
getAssetPrefix() {
|
|
747
|
-
|
|
715
|
+
const generatedAssetPrefix = generateAssetPrefixFromName({
|
|
716
|
+
name: this.name
|
|
717
|
+
});
|
|
718
|
+
if ("assetPrefix" in this.serialized) {
|
|
719
|
+
return this.serialized.assetPrefix ?? generatedAssetPrefix;
|
|
720
|
+
}
|
|
721
|
+
return generatedAssetPrefix;
|
|
748
722
|
}
|
|
749
723
|
getAutomationBypassEnvVarName() {
|
|
750
724
|
return generateAutomationBypassEnvVarName({ name: this.name });
|
|
@@ -878,7 +852,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
878
852
|
);
|
|
879
853
|
if (!app) {
|
|
880
854
|
throw new MicrofrontendError(
|
|
881
|
-
`Could not find microfrontends configuration for application "${name}"
|
|
855
|
+
`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.`,
|
|
882
856
|
{
|
|
883
857
|
type: "application",
|
|
884
858
|
subtype: "not_found"
|
|
@@ -923,23 +897,6 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
923
897
|
toSchemaJson() {
|
|
924
898
|
return this.serialized.config;
|
|
925
899
|
}
|
|
926
|
-
toClientConfig() {
|
|
927
|
-
const applications = Object.fromEntries(
|
|
928
|
-
Object.entries(this.childApplications).map(([name, application]) => [
|
|
929
|
-
name,
|
|
930
|
-
{
|
|
931
|
-
default: false,
|
|
932
|
-
routing: application.routing
|
|
933
|
-
}
|
|
934
|
-
])
|
|
935
|
-
);
|
|
936
|
-
applications[this.defaultApplication.name] = {
|
|
937
|
-
default: true
|
|
938
|
-
};
|
|
939
|
-
return new MicrofrontendConfigClient({
|
|
940
|
-
applications
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
900
|
serialize() {
|
|
944
901
|
return this.serialized;
|
|
945
902
|
}
|
|
@@ -952,8 +909,31 @@ function getApplicationContext(opts) {
|
|
|
952
909
|
if (opts?.appName) {
|
|
953
910
|
return { name: opts.appName };
|
|
954
911
|
}
|
|
912
|
+
if (process.env.VERCEL_PROJECT_NAME) {
|
|
913
|
+
return {
|
|
914
|
+
name: process.env.VERCEL_PROJECT_NAME,
|
|
915
|
+
projectName: process.env.VERCEL_PROJECT_NAME
|
|
916
|
+
};
|
|
917
|
+
}
|
|
955
918
|
if (process.env.NX_TASK_TARGET_PROJECT) {
|
|
956
|
-
return {
|
|
919
|
+
return {
|
|
920
|
+
name: process.env.NX_TASK_TARGET_PROJECT,
|
|
921
|
+
packageJsonName: process.env.NX_TASK_TARGET_PROJECT
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
try {
|
|
925
|
+
const vercelProjectJsonPath = fs5.readFileSync(
|
|
926
|
+
path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
|
|
927
|
+
"utf-8"
|
|
928
|
+
);
|
|
929
|
+
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
930
|
+
if (projectJson.projectName) {
|
|
931
|
+
return {
|
|
932
|
+
name: projectJson.projectName,
|
|
933
|
+
projectName: projectJson.projectName
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
} catch (_) {
|
|
957
937
|
}
|
|
958
938
|
try {
|
|
959
939
|
const packageJsonString = fs5.readFileSync(
|
|
@@ -971,7 +951,7 @@ function getApplicationContext(opts) {
|
|
|
971
951
|
}
|
|
972
952
|
);
|
|
973
953
|
}
|
|
974
|
-
return { name: packageJson.name };
|
|
954
|
+
return { name: packageJson.name, packageJsonName: packageJson.name };
|
|
975
955
|
} catch (err) {
|
|
976
956
|
throw MicrofrontendError.handle(err, {
|
|
977
957
|
fileName: "package.json"
|
|
@@ -1111,6 +1091,10 @@ var schema_default = {
|
|
|
1111
1091
|
routing: {
|
|
1112
1092
|
$ref: "#/definitions/Routing",
|
|
1113
1093
|
description: "Groups of path expressions that are routed to this application."
|
|
1094
|
+
},
|
|
1095
|
+
assetPrefix: {
|
|
1096
|
+
type: "string",
|
|
1097
|
+
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."
|
|
1114
1098
|
}
|
|
1115
1099
|
},
|
|
1116
1100
|
required: [
|
|
@@ -1336,7 +1320,7 @@ var MicrofrontendsServer = class {
|
|
|
1336
1320
|
}
|
|
1337
1321
|
try {
|
|
1338
1322
|
const packageRoot = findPackageRoot(directory);
|
|
1339
|
-
const
|
|
1323
|
+
const applicationContext = getApplicationContext({ packageRoot });
|
|
1340
1324
|
const maybeConfig = findConfig({ dir: packageRoot });
|
|
1341
1325
|
if (maybeConfig) {
|
|
1342
1326
|
return MicrofrontendsServer.fromFile({
|
|
@@ -1370,7 +1354,7 @@ var MicrofrontendsServer = class {
|
|
|
1370
1354
|
if (isMonorepo2) {
|
|
1371
1355
|
const defaultPackage = inferMicrofrontendsLocation({
|
|
1372
1356
|
repositoryRoot,
|
|
1373
|
-
|
|
1357
|
+
applicationContext
|
|
1374
1358
|
});
|
|
1375
1359
|
const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
|
|
1376
1360
|
if (maybeConfigFromDefault) {
|