@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
|
@@ -197,8 +197,9 @@ var CONFIGURATION_FILENAMES = [
|
|
|
197
197
|
var configCache = {};
|
|
198
198
|
function findPackageWithMicrofrontendsConfig({
|
|
199
199
|
repositoryRoot,
|
|
200
|
-
|
|
200
|
+
applicationContext
|
|
201
201
|
}) {
|
|
202
|
+
const applicationName = applicationContext.name;
|
|
202
203
|
try {
|
|
203
204
|
const microfrontendsJsonPaths = fg.globSync(
|
|
204
205
|
`**/{${CONFIGURATION_FILENAMES.join(",")}}`,
|
|
@@ -240,26 +241,45 @@ ${matchingPaths.join("\n \u2022 ")}`,
|
|
|
240
241
|
);
|
|
241
242
|
}
|
|
242
243
|
if (matchingPaths.length === 0) {
|
|
244
|
+
let additionalErrorMessage = "";
|
|
245
|
+
if (microfrontendsJsonPaths.length > 0) {
|
|
246
|
+
if (!applicationContext.projectName) {
|
|
247
|
+
additionalErrorMessage = `
|
|
248
|
+
|
|
249
|
+
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.`;
|
|
250
|
+
} else {
|
|
251
|
+
additionalErrorMessage = `
|
|
252
|
+
|
|
253
|
+
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
243
256
|
throw new MicrofrontendError(
|
|
244
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains "
|
|
257
|
+
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
258
|
+
|
|
259
|
+
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.
|
|
260
|
+
|
|
261
|
+
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
245
262
|
{ type: "config", subtype: "inference_failed" }
|
|
246
263
|
);
|
|
247
264
|
}
|
|
248
265
|
const [packageJsonPath] = matchingPaths;
|
|
249
266
|
return dirname(packageJsonPath);
|
|
250
267
|
} catch (error) {
|
|
268
|
+
if (error instanceof MicrofrontendError) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
251
271
|
return null;
|
|
252
272
|
}
|
|
253
273
|
}
|
|
254
274
|
function inferMicrofrontendsLocation(opts) {
|
|
255
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.
|
|
275
|
+
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}`;
|
|
256
276
|
if (configCache[cacheKey]) {
|
|
257
277
|
return configCache[cacheKey];
|
|
258
278
|
}
|
|
259
279
|
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
260
280
|
if (!result) {
|
|
261
281
|
throw new MicrofrontendError(
|
|
262
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.
|
|
282
|
+
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
263
283
|
{ type: "config", subtype: "inference_failed" }
|
|
264
284
|
);
|
|
265
285
|
}
|
|
@@ -331,90 +351,13 @@ function findConfig({ dir }) {
|
|
|
331
351
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
332
352
|
import { parse as parse2 } from "jsonc-parser";
|
|
333
353
|
|
|
334
|
-
// src/config/microfrontends-config/client/index.ts
|
|
335
|
-
import { pathToRegexp } from "path-to-regexp";
|
|
336
|
-
var regexpCache = /* @__PURE__ */ new Map();
|
|
337
|
-
var getRegexp = (path6) => {
|
|
338
|
-
const existing = regexpCache.get(path6);
|
|
339
|
-
if (existing) {
|
|
340
|
-
return existing;
|
|
341
|
-
}
|
|
342
|
-
const regexp = pathToRegexp(path6);
|
|
343
|
-
regexpCache.set(path6, regexp);
|
|
344
|
-
return regexp;
|
|
345
|
-
};
|
|
346
|
-
var MicrofrontendConfigClient = class {
|
|
347
|
-
constructor(config, opts) {
|
|
348
|
-
this.pathCache = {};
|
|
349
|
-
this.serialized = config;
|
|
350
|
-
if (opts?.removeFlaggedPaths) {
|
|
351
|
-
for (const app of Object.values(config.applications)) {
|
|
352
|
-
if (app.routing) {
|
|
353
|
-
app.routing = app.routing.filter((match) => !match.flag);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
this.applications = config.applications;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Create a new `MicrofrontendConfigClient` from a JSON string.
|
|
361
|
-
* Config must be passed in to remain framework agnostic
|
|
362
|
-
*/
|
|
363
|
-
static fromEnv(config, opts) {
|
|
364
|
-
if (!config) {
|
|
365
|
-
throw new Error(
|
|
366
|
-
"Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
return new MicrofrontendConfigClient(
|
|
370
|
-
JSON.parse(config),
|
|
371
|
-
opts
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
isEqual(other) {
|
|
375
|
-
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
376
|
-
}
|
|
377
|
-
getApplicationNameForPath(path6) {
|
|
378
|
-
if (!path6.startsWith("/")) {
|
|
379
|
-
throw new Error(`Path must start with a /`);
|
|
380
|
-
}
|
|
381
|
-
if (this.pathCache[path6]) {
|
|
382
|
-
return this.pathCache[path6];
|
|
383
|
-
}
|
|
384
|
-
const pathname = new URL(path6, "https://example.com").pathname;
|
|
385
|
-
for (const [name, application] of Object.entries(this.applications)) {
|
|
386
|
-
if (application.routing) {
|
|
387
|
-
for (const group of application.routing) {
|
|
388
|
-
for (const childPath of group.paths) {
|
|
389
|
-
const regexp = getRegexp(childPath);
|
|
390
|
-
if (regexp.test(pathname)) {
|
|
391
|
-
this.pathCache[path6] = name;
|
|
392
|
-
return name;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
const defaultApplication = Object.entries(this.applications).find(
|
|
399
|
-
([, application]) => application.default
|
|
400
|
-
);
|
|
401
|
-
if (!defaultApplication) {
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
this.pathCache[path6] = defaultApplication[0];
|
|
405
|
-
return defaultApplication[0];
|
|
406
|
-
}
|
|
407
|
-
serialize() {
|
|
408
|
-
return this.serialized;
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
|
|
412
354
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
413
|
-
import { pathToRegexp
|
|
355
|
+
import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
|
|
414
356
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
415
357
|
style: "long",
|
|
416
358
|
type: "conjunction"
|
|
417
359
|
});
|
|
360
|
+
var VALID_ASSET_PREFIX_REGEXP = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
418
361
|
var validateConfigPaths = (applicationConfigsById) => {
|
|
419
362
|
if (!applicationConfigsById) {
|
|
420
363
|
return;
|
|
@@ -437,7 +380,7 @@ var validateConfigPaths = (applicationConfigsById) => {
|
|
|
437
380
|
} else {
|
|
438
381
|
pathsByApplicationId.set(path6, {
|
|
439
382
|
applications: [id],
|
|
440
|
-
matcher:
|
|
383
|
+
matcher: pathToRegexp(path6),
|
|
441
384
|
applicationId: id
|
|
442
385
|
});
|
|
443
386
|
}
|
|
@@ -541,6 +484,22 @@ var validateAppPaths = (name, app) => {
|
|
|
541
484
|
}
|
|
542
485
|
}
|
|
543
486
|
}
|
|
487
|
+
if (app.assetPrefix) {
|
|
488
|
+
if (!VALID_ASSET_PREFIX_REGEXP.test(app.assetPrefix)) {
|
|
489
|
+
throw new MicrofrontendError(
|
|
490
|
+
`Invalid asset prefix for application "${name}". ${app.assetPrefix} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`,
|
|
491
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
if (app.assetPrefix !== `vc-ap-${name}` && !app.routing.some(
|
|
495
|
+
(group) => group.paths.includes(`/${app.assetPrefix}/:path*`) && !group.flag
|
|
496
|
+
)) {
|
|
497
|
+
throw new MicrofrontendError(
|
|
498
|
+
`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.`,
|
|
499
|
+
{ type: "application", subtype: "invalid_asset_prefix" }
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
544
503
|
};
|
|
545
504
|
var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
546
505
|
if (!applicationConfigsById) {
|
|
@@ -572,6 +531,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
572
531
|
}
|
|
573
532
|
};
|
|
574
533
|
|
|
534
|
+
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
535
|
+
import md5 from "md5";
|
|
536
|
+
function hashApplicationName(name) {
|
|
537
|
+
if (!name) {
|
|
538
|
+
throw new Error("Application name is required to generate hash");
|
|
539
|
+
}
|
|
540
|
+
return md5(name).substring(0, 6).padStart(6, "0");
|
|
541
|
+
}
|
|
542
|
+
|
|
575
543
|
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
576
544
|
var PREFIX = "vc-ap";
|
|
577
545
|
function generateAssetPrefixFromName({
|
|
@@ -580,7 +548,7 @@ function generateAssetPrefixFromName({
|
|
|
580
548
|
if (!name) {
|
|
581
549
|
throw new Error("Name is required to generate an asset prefix");
|
|
582
550
|
}
|
|
583
|
-
return `${PREFIX}-${name}`;
|
|
551
|
+
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
584
552
|
}
|
|
585
553
|
|
|
586
554
|
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
@@ -740,7 +708,13 @@ var Application = class {
|
|
|
740
708
|
return this.default;
|
|
741
709
|
}
|
|
742
710
|
getAssetPrefix() {
|
|
743
|
-
|
|
711
|
+
const generatedAssetPrefix = generateAssetPrefixFromName({
|
|
712
|
+
name: this.name
|
|
713
|
+
});
|
|
714
|
+
if ("assetPrefix" in this.serialized) {
|
|
715
|
+
return this.serialized.assetPrefix ?? generatedAssetPrefix;
|
|
716
|
+
}
|
|
717
|
+
return generatedAssetPrefix;
|
|
744
718
|
}
|
|
745
719
|
getAutomationBypassEnvVarName() {
|
|
746
720
|
return generateAutomationBypassEnvVarName({ name: this.name });
|
|
@@ -874,7 +848,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
874
848
|
);
|
|
875
849
|
if (!app) {
|
|
876
850
|
throw new MicrofrontendError(
|
|
877
|
-
`Could not find microfrontends configuration for application "${name}"
|
|
851
|
+
`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.`,
|
|
878
852
|
{
|
|
879
853
|
type: "application",
|
|
880
854
|
subtype: "not_found"
|
|
@@ -919,23 +893,6 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
919
893
|
toSchemaJson() {
|
|
920
894
|
return this.serialized.config;
|
|
921
895
|
}
|
|
922
|
-
toClientConfig() {
|
|
923
|
-
const applications = Object.fromEntries(
|
|
924
|
-
Object.entries(this.childApplications).map(([name, application]) => [
|
|
925
|
-
name,
|
|
926
|
-
{
|
|
927
|
-
default: false,
|
|
928
|
-
routing: application.routing
|
|
929
|
-
}
|
|
930
|
-
])
|
|
931
|
-
);
|
|
932
|
-
applications[this.defaultApplication.name] = {
|
|
933
|
-
default: true
|
|
934
|
-
};
|
|
935
|
-
return new MicrofrontendConfigClient({
|
|
936
|
-
applications
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
896
|
serialize() {
|
|
940
897
|
return this.serialized;
|
|
941
898
|
}
|
|
@@ -948,8 +905,31 @@ function getApplicationContext(opts) {
|
|
|
948
905
|
if (opts?.appName) {
|
|
949
906
|
return { name: opts.appName };
|
|
950
907
|
}
|
|
908
|
+
if (process.env.VERCEL_PROJECT_NAME) {
|
|
909
|
+
return {
|
|
910
|
+
name: process.env.VERCEL_PROJECT_NAME,
|
|
911
|
+
projectName: process.env.VERCEL_PROJECT_NAME
|
|
912
|
+
};
|
|
913
|
+
}
|
|
951
914
|
if (process.env.NX_TASK_TARGET_PROJECT) {
|
|
952
|
-
return {
|
|
915
|
+
return {
|
|
916
|
+
name: process.env.NX_TASK_TARGET_PROJECT,
|
|
917
|
+
packageJsonName: process.env.NX_TASK_TARGET_PROJECT
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
const vercelProjectJsonPath = fs5.readFileSync(
|
|
922
|
+
path4.join(opts?.packageRoot || ".", ".vercel", "project.json"),
|
|
923
|
+
"utf-8"
|
|
924
|
+
);
|
|
925
|
+
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
926
|
+
if (projectJson.projectName) {
|
|
927
|
+
return {
|
|
928
|
+
name: projectJson.projectName,
|
|
929
|
+
projectName: projectJson.projectName
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
} catch (_) {
|
|
953
933
|
}
|
|
954
934
|
try {
|
|
955
935
|
const packageJsonString = fs5.readFileSync(
|
|
@@ -967,7 +947,7 @@ function getApplicationContext(opts) {
|
|
|
967
947
|
}
|
|
968
948
|
);
|
|
969
949
|
}
|
|
970
|
-
return { name: packageJson.name };
|
|
950
|
+
return { name: packageJson.name, packageJsonName: packageJson.name };
|
|
971
951
|
} catch (err) {
|
|
972
952
|
throw MicrofrontendError.handle(err, {
|
|
973
953
|
fileName: "package.json"
|
|
@@ -1107,6 +1087,10 @@ var schema_default = {
|
|
|
1107
1087
|
routing: {
|
|
1108
1088
|
$ref: "#/definitions/Routing",
|
|
1109
1089
|
description: "Groups of path expressions that are routed to this application."
|
|
1090
|
+
},
|
|
1091
|
+
assetPrefix: {
|
|
1092
|
+
type: "string",
|
|
1093
|
+
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."
|
|
1110
1094
|
}
|
|
1111
1095
|
},
|
|
1112
1096
|
required: [
|
|
@@ -1332,7 +1316,7 @@ var MicrofrontendsServer = class {
|
|
|
1332
1316
|
}
|
|
1333
1317
|
try {
|
|
1334
1318
|
const packageRoot = findPackageRoot(directory);
|
|
1335
|
-
const
|
|
1319
|
+
const applicationContext = getApplicationContext({ packageRoot });
|
|
1336
1320
|
const maybeConfig = findConfig({ dir: packageRoot });
|
|
1337
1321
|
if (maybeConfig) {
|
|
1338
1322
|
return MicrofrontendsServer.fromFile({
|
|
@@ -1366,7 +1350,7 @@ var MicrofrontendsServer = class {
|
|
|
1366
1350
|
if (isMonorepo2) {
|
|
1367
1351
|
const defaultPackage = inferMicrofrontendsLocation({
|
|
1368
1352
|
repositoryRoot,
|
|
1369
|
-
|
|
1353
|
+
applicationContext
|
|
1370
1354
|
});
|
|
1371
1355
|
const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
|
|
1372
1356
|
if (maybeConfigFromDefault) {
|