@vercel/microfrontends 2.2.0 → 2.2.2
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 +25 -0
- package/cli/index.cjs +0 -1
- package/dist/bin/cli.cjs +551 -381
- package/dist/config.cjs +191 -169
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js +192 -170
- package/dist/config.js.map +1 -1
- package/dist/experimental/sveltekit.cjs +643 -489
- package/dist/experimental/sveltekit.cjs.map +1 -1
- package/dist/experimental/sveltekit.js +645 -491
- package/dist/experimental/sveltekit.js.map +1 -1
- package/dist/experimental/vite.cjs +671 -519
- package/dist/experimental/vite.cjs.map +1 -1
- package/dist/experimental/vite.js +669 -517
- package/dist/experimental/vite.js.map +1 -1
- package/dist/microfrontends/server.cjs +661 -509
- package/dist/microfrontends/server.cjs.map +1 -1
- package/dist/microfrontends/server.d.ts +2 -2
- package/dist/microfrontends/server.js +663 -511
- package/dist/microfrontends/server.js.map +1 -1
- package/dist/microfrontends/utils.cjs +117 -23
- package/dist/microfrontends/utils.cjs.map +1 -1
- package/dist/microfrontends/utils.d.ts +4 -4
- package/dist/microfrontends/utils.js +118 -24
- 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.d.ts +8 -8
- package/dist/next/client.js +1 -1
- package/dist/next/client.js.map +1 -1
- package/dist/next/config.cjs +784 -626
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js +782 -624
- package/dist/next/config.js.map +1 -1
- package/dist/next/middleware.cjs +244 -222
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.js +245 -223
- package/dist/next/middleware.js.map +1 -1
- package/dist/next/testing.cjs +192 -170
- package/dist/next/testing.cjs.map +1 -1
- package/dist/next/testing.d.ts +1 -1
- package/dist/next/testing.js +193 -171
- package/dist/next/testing.js.map +1 -1
- package/dist/overrides.cjs +5 -5
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.d.ts +9 -9
- package/dist/overrides.js +5 -5
- package/dist/overrides.js.map +1 -1
- package/dist/utils/mfe-port.cjs +689 -522
- package/dist/utils/mfe-port.cjs.map +1 -1
- package/dist/utils/mfe-port.d.ts +9 -1
- package/dist/utils/mfe-port.js +687 -521
- package/dist/utils/mfe-port.js.map +1 -1
- package/dist/validation.cjs +8 -24
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.js +8 -24
- package/dist/validation.js.map +1 -1
- package/package.json +4 -6
package/dist/next/config.js
CHANGED
|
@@ -43,40 +43,7 @@ function displayLocalProxyInfo(port) {
|
|
|
43
43
|
|
|
44
44
|
// src/config/microfrontends/server/index.ts
|
|
45
45
|
import fs6 from "node:fs";
|
|
46
|
-
import { dirname as dirname2, join as
|
|
47
|
-
|
|
48
|
-
// src/config/overrides/constants.ts
|
|
49
|
-
var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
|
|
50
|
-
var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
|
|
51
|
-
|
|
52
|
-
// src/config/overrides/is-override-cookie.ts
|
|
53
|
-
function isOverrideCookie(cookie) {
|
|
54
|
-
return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/config/overrides/get-override-from-cookie.ts
|
|
58
|
-
function getOverrideFromCookie(cookie) {
|
|
59
|
-
if (!isOverrideCookie(cookie) || !cookie.value)
|
|
60
|
-
return;
|
|
61
|
-
return {
|
|
62
|
-
application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
|
|
63
|
-
host: cookie.value
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// src/config/overrides/parse-overrides.ts
|
|
68
|
-
function parseOverrides(cookies) {
|
|
69
|
-
const overridesConfig = { applications: {} };
|
|
70
|
-
cookies.forEach((cookie) => {
|
|
71
|
-
const override = getOverrideFromCookie(cookie);
|
|
72
|
-
if (!override)
|
|
73
|
-
return;
|
|
74
|
-
overridesConfig.applications[override.application] = {
|
|
75
|
-
environment: { host: override.host }
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
return overridesConfig;
|
|
79
|
-
}
|
|
46
|
+
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
80
47
|
|
|
81
48
|
// src/config/errors.ts
|
|
82
49
|
var MicrofrontendError = class extends Error {
|
|
@@ -170,253 +137,47 @@ var MicrofrontendError = class extends Error {
|
|
|
170
137
|
}
|
|
171
138
|
};
|
|
172
139
|
|
|
173
|
-
// src/config/microfrontends-config/
|
|
174
|
-
function getConfigStringFromEnv() {
|
|
175
|
-
const config = process.env.MFE_CONFIG;
|
|
176
|
-
if (!config) {
|
|
177
|
-
throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
|
|
178
|
-
type: "config",
|
|
179
|
-
subtype: "not_found_in_env"
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return config;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// src/config/schema/utils/is-default-app.ts
|
|
186
|
-
function isDefaultApp(a) {
|
|
187
|
-
return !("routing" in a);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// src/config/microfrontends/utils/find-repository-root.ts
|
|
191
|
-
import fs from "node:fs";
|
|
192
|
-
import path from "node:path";
|
|
193
|
-
var GIT_DIRECTORY = ".git";
|
|
194
|
-
function hasGitDirectory(dir) {
|
|
195
|
-
const gitPath = path.join(dir, GIT_DIRECTORY);
|
|
196
|
-
return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
|
|
197
|
-
}
|
|
198
|
-
function hasPnpmWorkspaces(dir) {
|
|
199
|
-
return fs.existsSync(path.join(dir, "pnpm-workspace.yaml"));
|
|
200
|
-
}
|
|
201
|
-
function hasPackageJson(dir) {
|
|
202
|
-
return fs.existsSync(path.join(dir, "package.json"));
|
|
203
|
-
}
|
|
204
|
-
function findRepositoryRoot(startDir) {
|
|
205
|
-
if (process.env.NX_WORKSPACE_ROOT) {
|
|
206
|
-
return process.env.NX_WORKSPACE_ROOT;
|
|
207
|
-
}
|
|
208
|
-
let currentDir = startDir || process.cwd();
|
|
209
|
-
let lastPackageJsonDir = null;
|
|
210
|
-
while (currentDir !== path.parse(currentDir).root) {
|
|
211
|
-
if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
|
|
212
|
-
return currentDir;
|
|
213
|
-
}
|
|
214
|
-
if (hasPackageJson(currentDir)) {
|
|
215
|
-
lastPackageJsonDir = currentDir;
|
|
216
|
-
}
|
|
217
|
-
currentDir = path.dirname(currentDir);
|
|
218
|
-
}
|
|
219
|
-
if (lastPackageJsonDir) {
|
|
220
|
-
return lastPackageJsonDir;
|
|
221
|
-
}
|
|
222
|
-
throw new Error(
|
|
223
|
-
`Could not find the root of the repository for ${startDir}. Please ensure that the directory is part of a Git repository. If you suspect that this should work, please file an issue to the Vercel team.`
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// src/config/microfrontends/utils/infer-microfrontends-location.ts
|
|
228
|
-
import { dirname } from "node:path";
|
|
229
|
-
import { readFileSync } from "node:fs";
|
|
140
|
+
// src/config/microfrontends-config/isomorphic/index.ts
|
|
230
141
|
import { parse } from "jsonc-parser";
|
|
231
|
-
import fg from "fast-glob";
|
|
232
142
|
|
|
233
|
-
// src/config/
|
|
234
|
-
var
|
|
235
|
-
|
|
236
|
-
"microfrontends.jsonc"
|
|
237
|
-
];
|
|
238
|
-
function getPossibleConfigurationFilenames({
|
|
239
|
-
customConfigFilename
|
|
240
|
-
}) {
|
|
241
|
-
if (customConfigFilename) {
|
|
242
|
-
if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
|
|
243
|
-
throw new Error(
|
|
244
|
-
`Found VC_MICROFRONTENDS_CONFIG_FILE_NAME but the name is invalid. Received: ${customConfigFilename}. The file name must end with '.json' or '.jsonc'. It's also possible for the env var to include the path, eg microfrontends-dev.json or /path/to/microfrontends-dev.json.`
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
return Array.from(
|
|
248
|
-
/* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
return DEFAULT_CONFIGURATION_FILENAMES;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// src/config/microfrontends/utils/infer-microfrontends-location.ts
|
|
255
|
-
var configCache = {};
|
|
256
|
-
function findPackageWithMicrofrontendsConfig({
|
|
257
|
-
repositoryRoot,
|
|
258
|
-
applicationContext,
|
|
259
|
-
customConfigFilename
|
|
260
|
-
}) {
|
|
261
|
-
const applicationName = applicationContext.name;
|
|
262
|
-
try {
|
|
263
|
-
const microfrontendsJsonPaths = fg.globSync(
|
|
264
|
-
`**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
|
|
265
|
-
{
|
|
266
|
-
cwd: repositoryRoot,
|
|
267
|
-
absolute: true,
|
|
268
|
-
onlyFiles: true,
|
|
269
|
-
followSymbolicLinks: false,
|
|
270
|
-
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
271
|
-
}
|
|
272
|
-
);
|
|
273
|
-
const matchingPaths = [];
|
|
274
|
-
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
275
|
-
try {
|
|
276
|
-
const microfrontendsJsonContent = readFileSync(
|
|
277
|
-
microfrontendsJsonPath,
|
|
278
|
-
"utf-8"
|
|
279
|
-
);
|
|
280
|
-
const microfrontendsJson = parse(microfrontendsJsonContent);
|
|
281
|
-
if (microfrontendsJson.applications[applicationName]) {
|
|
282
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
283
|
-
} else {
|
|
284
|
-
for (const [_, app] of Object.entries(
|
|
285
|
-
microfrontendsJson.applications
|
|
286
|
-
)) {
|
|
287
|
-
if (app.packageName === applicationName) {
|
|
288
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
} catch (error2) {
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
if (matchingPaths.length > 1) {
|
|
296
|
-
throw new MicrofrontendError(
|
|
297
|
-
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
|
|
298
|
-
${matchingPaths.join("\n \u2022 ")}`,
|
|
299
|
-
{ type: "config", subtype: "inference_failed" }
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
if (matchingPaths.length === 0) {
|
|
303
|
-
let additionalErrorMessage = "";
|
|
304
|
-
if (microfrontendsJsonPaths.length > 0) {
|
|
305
|
-
if (!applicationContext.projectName) {
|
|
306
|
-
additionalErrorMessage = `
|
|
307
|
-
|
|
308
|
-
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.`;
|
|
309
|
-
} else {
|
|
310
|
-
additionalErrorMessage = `
|
|
311
|
-
|
|
312
|
-
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
throw new MicrofrontendError(
|
|
316
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
317
|
-
|
|
318
|
-
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.
|
|
319
|
-
|
|
320
|
-
If your Vercel Microfrontends configuration has a custom name, ensure the VC_MICROFRONTENDS_CONFIG_FILE_NAME environment variable is set, you can pull the vercel project environment variables using the "vercel env pull" command.
|
|
143
|
+
// src/config/overrides/constants.ts
|
|
144
|
+
var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
|
|
145
|
+
var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
|
|
321
146
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
const [packageJsonPath] = matchingPaths;
|
|
327
|
-
return dirname(packageJsonPath);
|
|
328
|
-
} catch (error2) {
|
|
329
|
-
if (error2 instanceof MicrofrontendError) {
|
|
330
|
-
throw error2;
|
|
331
|
-
}
|
|
332
|
-
return null;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function inferMicrofrontendsLocation(opts) {
|
|
336
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
|
|
337
|
-
if (configCache[cacheKey]) {
|
|
338
|
-
return configCache[cacheKey];
|
|
339
|
-
}
|
|
340
|
-
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
341
|
-
if (!result) {
|
|
342
|
-
throw new MicrofrontendError(
|
|
343
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
344
|
-
{ type: "config", subtype: "inference_failed" }
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
configCache[cacheKey] = result;
|
|
348
|
-
return result;
|
|
147
|
+
// src/config/overrides/is-override-cookie.ts
|
|
148
|
+
function isOverrideCookie(cookie) {
|
|
149
|
+
return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
|
|
349
150
|
}
|
|
350
151
|
|
|
351
|
-
// src/config/
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
|
|
365
|
-
return true;
|
|
366
|
-
}
|
|
367
|
-
const packageJsonPath = path2.join(repositoryRoot, "package.json");
|
|
368
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
const packageJson = JSON.parse(
|
|
372
|
-
fs2.readFileSync(packageJsonPath, "utf-8")
|
|
373
|
-
);
|
|
374
|
-
return packageJson.workspaces !== void 0;
|
|
375
|
-
} catch (error2) {
|
|
376
|
-
logger.error("Error determining if repository is a monorepo", error2);
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
152
|
+
// src/config/overrides/get-override-from-cookie.ts
|
|
153
|
+
function getOverrideFromCookie(cookie) {
|
|
154
|
+
if (!isOverrideCookie(cookie) || !cookie.value)
|
|
155
|
+
return;
|
|
156
|
+
return {
|
|
157
|
+
application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
|
|
158
|
+
host: cookie.value
|
|
159
|
+
};
|
|
379
160
|
}
|
|
380
161
|
|
|
381
|
-
// src/config/
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
throw new Error(
|
|
395
|
-
`The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
|
|
396
|
-
);
|
|
162
|
+
// src/config/overrides/parse-overrides.ts
|
|
163
|
+
function parseOverrides(cookies) {
|
|
164
|
+
const overridesConfig = { applications: {} };
|
|
165
|
+
cookies.forEach((cookie) => {
|
|
166
|
+
const override = getOverrideFromCookie(cookie);
|
|
167
|
+
if (!override)
|
|
168
|
+
return;
|
|
169
|
+
overridesConfig.applications[override.application] = {
|
|
170
|
+
environment: { host: override.host }
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
return overridesConfig;
|
|
397
174
|
}
|
|
398
175
|
|
|
399
|
-
// src/config/
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
function findConfig({
|
|
403
|
-
dir,
|
|
404
|
-
customConfigFilename
|
|
405
|
-
}) {
|
|
406
|
-
for (const filename of getPossibleConfigurationFilenames({
|
|
407
|
-
customConfigFilename
|
|
408
|
-
})) {
|
|
409
|
-
const maybeConfig = join(dir, filename);
|
|
410
|
-
if (fs4.existsSync(maybeConfig)) {
|
|
411
|
-
return maybeConfig;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return null;
|
|
176
|
+
// src/config/schema/utils/is-default-app.ts
|
|
177
|
+
function isDefaultApp(a) {
|
|
178
|
+
return !("routing" in a);
|
|
415
179
|
}
|
|
416
180
|
|
|
417
|
-
// src/config/microfrontends-config/isomorphic/index.ts
|
|
418
|
-
import { parse as parse2 } from "jsonc-parser";
|
|
419
|
-
|
|
420
181
|
// src/config/microfrontends-config/client/index.ts
|
|
421
182
|
import { pathToRegexp } from "path-to-regexp";
|
|
422
183
|
var regexpCache = /* @__PURE__ */ new Map();
|
|
@@ -477,46 +238,223 @@ var MicrofrontendConfigClient = class {
|
|
|
477
238
|
}
|
|
478
239
|
return new MicrofrontendConfigClient(JSON.parse(config));
|
|
479
240
|
}
|
|
480
|
-
isEqual(other) {
|
|
481
|
-
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
241
|
+
isEqual(other) {
|
|
242
|
+
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
243
|
+
}
|
|
244
|
+
getApplicationNameForPath(path6) {
|
|
245
|
+
if (!path6.startsWith("/")) {
|
|
246
|
+
throw new Error(`Path must start with a /`);
|
|
247
|
+
}
|
|
248
|
+
if (this.pathCache[path6]) {
|
|
249
|
+
return this.pathCache[path6];
|
|
250
|
+
}
|
|
251
|
+
const pathname = new URL(path6, "https://example.com").pathname;
|
|
252
|
+
for (const [name, application] of Object.entries(this.applications)) {
|
|
253
|
+
if (application.routing) {
|
|
254
|
+
for (const group of application.routing) {
|
|
255
|
+
for (const childPath of group.paths) {
|
|
256
|
+
const regexp = getRegexp(childPath);
|
|
257
|
+
if (regexp.test(pathname)) {
|
|
258
|
+
this.pathCache[path6] = name;
|
|
259
|
+
return name;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const defaultApplication = Object.entries(this.applications).find(
|
|
266
|
+
([, application]) => application.default
|
|
267
|
+
);
|
|
268
|
+
if (!defaultApplication) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
this.pathCache[path6] = defaultApplication[0];
|
|
272
|
+
return defaultApplication[0];
|
|
273
|
+
}
|
|
274
|
+
serialize() {
|
|
275
|
+
return this.serialized;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// src/config/microfrontends-config/utils/get-config-from-env.ts
|
|
280
|
+
function getConfigStringFromEnv() {
|
|
281
|
+
const config = process.env.MFE_CONFIG;
|
|
282
|
+
if (!config) {
|
|
283
|
+
throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
|
|
284
|
+
type: "config",
|
|
285
|
+
subtype: "not_found_in_env"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return config;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/config/microfrontends-config/isomorphic/constants.ts
|
|
292
|
+
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
293
|
+
var MFE_APP_PORT_ENV = "MFE_APP_PORT";
|
|
294
|
+
var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
|
|
295
|
+
|
|
296
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
297
|
+
function generatePortFromName({
|
|
298
|
+
name,
|
|
299
|
+
minPort = 3e3,
|
|
300
|
+
maxPort = 8e3
|
|
301
|
+
}) {
|
|
302
|
+
if (!name) {
|
|
303
|
+
throw new Error("Name is required to generate a port");
|
|
304
|
+
}
|
|
305
|
+
let hash = 0;
|
|
306
|
+
for (let i = 0; i < name.length; i++) {
|
|
307
|
+
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
308
|
+
hash |= 0;
|
|
309
|
+
}
|
|
310
|
+
hash = Math.abs(hash);
|
|
311
|
+
const range = maxPort - minPort;
|
|
312
|
+
const port = minPort + hash % range;
|
|
313
|
+
return port;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/config/microfrontends-config/isomorphic/host.ts
|
|
317
|
+
var Host = class {
|
|
318
|
+
constructor(hostConfig, options) {
|
|
319
|
+
if (typeof hostConfig === "string") {
|
|
320
|
+
({
|
|
321
|
+
protocol: this.protocol,
|
|
322
|
+
host: this.host,
|
|
323
|
+
port: this.port
|
|
324
|
+
} = Host.parseUrl(hostConfig));
|
|
325
|
+
} else {
|
|
326
|
+
const { protocol = "https", host, port } = hostConfig;
|
|
327
|
+
this.protocol = protocol;
|
|
328
|
+
this.host = host;
|
|
329
|
+
this.port = port;
|
|
330
|
+
}
|
|
331
|
+
this.local = options?.isLocal;
|
|
332
|
+
}
|
|
333
|
+
static parseUrl(url, defaultProtocol = "https") {
|
|
334
|
+
let hostToParse = url;
|
|
335
|
+
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
336
|
+
hostToParse = `${defaultProtocol}://${hostToParse}`;
|
|
337
|
+
}
|
|
338
|
+
const parsed = new URL(hostToParse);
|
|
339
|
+
if (!parsed.hostname) {
|
|
340
|
+
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
341
|
+
}
|
|
342
|
+
if (parsed.hash) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (parsed.username || parsed.password) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
Host.getMicrofrontendsError(
|
|
350
|
+
url,
|
|
351
|
+
"cannot have authentication credentials (username and/or password)"
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (parsed.pathname !== "/") {
|
|
356
|
+
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
357
|
+
}
|
|
358
|
+
if (parsed.search) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
const protocol = parsed.protocol.slice(0, -1);
|
|
364
|
+
return {
|
|
365
|
+
protocol,
|
|
366
|
+
host: parsed.hostname,
|
|
367
|
+
port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
static getMicrofrontendsError(url, message) {
|
|
371
|
+
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
372
|
+
}
|
|
373
|
+
isLocal() {
|
|
374
|
+
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
375
|
+
}
|
|
376
|
+
toString() {
|
|
377
|
+
const url = this.toUrl();
|
|
378
|
+
return url.toString().replace(/\/$/, "");
|
|
379
|
+
}
|
|
380
|
+
toUrl() {
|
|
381
|
+
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
382
|
+
return new URL(url);
|
|
482
383
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
if (
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
384
|
+
};
|
|
385
|
+
var LocalHost = class extends Host {
|
|
386
|
+
constructor({
|
|
387
|
+
appName,
|
|
388
|
+
local
|
|
389
|
+
}) {
|
|
390
|
+
const portOverride = process.env[MFE_APP_PORT_ENV];
|
|
391
|
+
if (portOverride) {
|
|
392
|
+
const overridePort = Number.parseInt(portOverride, 10);
|
|
393
|
+
if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
|
|
394
|
+
super({
|
|
395
|
+
protocol: "http",
|
|
396
|
+
host: "localhost",
|
|
397
|
+
port: overridePort
|
|
398
|
+
});
|
|
399
|
+
return;
|
|
502
400
|
}
|
|
503
401
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (
|
|
508
|
-
|
|
402
|
+
let protocol;
|
|
403
|
+
let host;
|
|
404
|
+
let port;
|
|
405
|
+
if (typeof local === "number") {
|
|
406
|
+
port = local;
|
|
407
|
+
} else if (typeof local === "string") {
|
|
408
|
+
if (/^\d+$/.test(local)) {
|
|
409
|
+
port = Number.parseInt(local, 10);
|
|
410
|
+
} else {
|
|
411
|
+
const parsed = Host.parseUrl(local, "http");
|
|
412
|
+
protocol = parsed.protocol;
|
|
413
|
+
host = parsed.host;
|
|
414
|
+
port = parsed.port;
|
|
415
|
+
}
|
|
416
|
+
} else if (local) {
|
|
417
|
+
protocol = local.protocol;
|
|
418
|
+
host = local.host;
|
|
419
|
+
port = local.port;
|
|
509
420
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
421
|
+
super({
|
|
422
|
+
protocol: protocol ?? "http",
|
|
423
|
+
host: host ?? "localhost",
|
|
424
|
+
port: port ?? generatePortFromName({ name: appName })
|
|
425
|
+
});
|
|
515
426
|
}
|
|
516
427
|
};
|
|
517
428
|
|
|
429
|
+
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
430
|
+
import md5 from "md5";
|
|
431
|
+
function hashApplicationName(name) {
|
|
432
|
+
if (!name) {
|
|
433
|
+
throw new Error("Application name is required to generate hash");
|
|
434
|
+
}
|
|
435
|
+
return md5(name).substring(0, 6).padStart(6, "0");
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
439
|
+
var PREFIX = "vc-ap";
|
|
440
|
+
function generateAssetPrefixFromName({
|
|
441
|
+
name
|
|
442
|
+
}) {
|
|
443
|
+
if (!name) {
|
|
444
|
+
throw new Error("Name is required to generate an asset prefix");
|
|
445
|
+
}
|
|
446
|
+
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
|
|
450
|
+
function generateAutomationBypassEnvVarName({
|
|
451
|
+
name
|
|
452
|
+
}) {
|
|
453
|
+
return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
454
|
+
}
|
|
455
|
+
|
|
518
456
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
519
|
-
import {
|
|
457
|
+
import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
|
|
520
458
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
521
459
|
style: "long",
|
|
522
460
|
type: "conjunction"
|
|
@@ -670,180 +608,32 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
670
608
|
if (!applicationConfigsById) {
|
|
671
609
|
return;
|
|
672
610
|
}
|
|
673
|
-
const applicationsWithoutRouting = Object.entries(
|
|
674
|
-
applicationConfigsById
|
|
675
|
-
).filter(([, app]) => isDefaultApp(app));
|
|
676
|
-
const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
|
|
677
|
-
(acc) => {
|
|
678
|
-
return acc + 1;
|
|
679
|
-
},
|
|
680
|
-
0
|
|
681
|
-
);
|
|
682
|
-
if (numApplicationsWithoutRouting === 0) {
|
|
683
|
-
throw new MicrofrontendError(
|
|
684
|
-
"No default application found. At least one application needs to be the default by omitting routing.",
|
|
685
|
-
{ type: "config", subtype: "no_default_application" }
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
if (numApplicationsWithoutRouting > 1) {
|
|
689
|
-
const applicationNamesMissingRouting = applicationsWithoutRouting.map(
|
|
690
|
-
([name]) => name
|
|
691
|
-
);
|
|
692
|
-
throw new MicrofrontendError(
|
|
693
|
-
`All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
|
|
694
|
-
{ type: "config", subtype: "multiple_default_applications" }
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
700
|
-
import md5 from "md5";
|
|
701
|
-
function hashApplicationName(name) {
|
|
702
|
-
if (!name) {
|
|
703
|
-
throw new Error("Application name is required to generate hash");
|
|
704
|
-
}
|
|
705
|
-
return md5(name).substring(0, 6).padStart(6, "0");
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
709
|
-
var PREFIX = "vc-ap";
|
|
710
|
-
function generateAssetPrefixFromName({
|
|
711
|
-
name
|
|
712
|
-
}) {
|
|
713
|
-
if (!name) {
|
|
714
|
-
throw new Error("Name is required to generate an asset prefix");
|
|
715
|
-
}
|
|
716
|
-
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
720
|
-
function generatePortFromName({
|
|
721
|
-
name,
|
|
722
|
-
minPort = 3e3,
|
|
723
|
-
maxPort = 8e3
|
|
724
|
-
}) {
|
|
725
|
-
if (!name) {
|
|
726
|
-
throw new Error("Name is required to generate a port");
|
|
727
|
-
}
|
|
728
|
-
let hash = 0;
|
|
729
|
-
for (let i = 0; i < name.length; i++) {
|
|
730
|
-
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
731
|
-
hash |= 0;
|
|
732
|
-
}
|
|
733
|
-
hash = Math.abs(hash);
|
|
734
|
-
const range = maxPort - minPort;
|
|
735
|
-
const port = minPort + hash % range;
|
|
736
|
-
return port;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
// src/config/microfrontends-config/isomorphic/host.ts
|
|
740
|
-
var Host = class {
|
|
741
|
-
constructor(hostConfig, options) {
|
|
742
|
-
if (typeof hostConfig === "string") {
|
|
743
|
-
({
|
|
744
|
-
protocol: this.protocol,
|
|
745
|
-
host: this.host,
|
|
746
|
-
port: this.port
|
|
747
|
-
} = Host.parseUrl(hostConfig));
|
|
748
|
-
} else {
|
|
749
|
-
const { protocol = "https", host, port } = hostConfig;
|
|
750
|
-
this.protocol = protocol;
|
|
751
|
-
this.host = host;
|
|
752
|
-
this.port = port;
|
|
753
|
-
}
|
|
754
|
-
this.local = options?.isLocal;
|
|
755
|
-
}
|
|
756
|
-
static parseUrl(url, defaultProtocol = "https") {
|
|
757
|
-
let hostToParse = url;
|
|
758
|
-
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
759
|
-
hostToParse = `${defaultProtocol}://${hostToParse}`;
|
|
760
|
-
}
|
|
761
|
-
const parsed = new URL(hostToParse);
|
|
762
|
-
if (!parsed.hostname) {
|
|
763
|
-
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
764
|
-
}
|
|
765
|
-
if (parsed.hash) {
|
|
766
|
-
throw new Error(
|
|
767
|
-
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
if (parsed.username || parsed.password) {
|
|
771
|
-
throw new Error(
|
|
772
|
-
Host.getMicrofrontendsError(
|
|
773
|
-
url,
|
|
774
|
-
"cannot have authentication credentials (username and/or password)"
|
|
775
|
-
)
|
|
776
|
-
);
|
|
777
|
-
}
|
|
778
|
-
if (parsed.pathname !== "/") {
|
|
779
|
-
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
780
|
-
}
|
|
781
|
-
if (parsed.search) {
|
|
782
|
-
throw new Error(
|
|
783
|
-
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
784
|
-
);
|
|
785
|
-
}
|
|
786
|
-
const protocol = parsed.protocol.slice(0, -1);
|
|
787
|
-
return {
|
|
788
|
-
protocol,
|
|
789
|
-
host: parsed.hostname,
|
|
790
|
-
port: parsed.port ? Number.parseInt(parsed.port) : void 0
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
static getMicrofrontendsError(url, message) {
|
|
794
|
-
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
795
|
-
}
|
|
796
|
-
isLocal() {
|
|
797
|
-
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
798
|
-
}
|
|
799
|
-
toString() {
|
|
800
|
-
const url = this.toUrl();
|
|
801
|
-
return url.toString().replace(/\/$/, "");
|
|
802
|
-
}
|
|
803
|
-
toUrl() {
|
|
804
|
-
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
805
|
-
return new URL(url);
|
|
806
|
-
}
|
|
807
|
-
};
|
|
808
|
-
var LocalHost = class extends Host {
|
|
809
|
-
constructor({
|
|
810
|
-
appName,
|
|
811
|
-
local
|
|
812
|
-
}) {
|
|
813
|
-
let protocol;
|
|
814
|
-
let host;
|
|
815
|
-
let port;
|
|
816
|
-
if (typeof local === "number") {
|
|
817
|
-
port = local;
|
|
818
|
-
} else if (typeof local === "string") {
|
|
819
|
-
if (/^\d+$/.test(local)) {
|
|
820
|
-
port = Number.parseInt(local);
|
|
821
|
-
} else {
|
|
822
|
-
const parsed = Host.parseUrl(local, "http");
|
|
823
|
-
protocol = parsed.protocol;
|
|
824
|
-
host = parsed.host;
|
|
825
|
-
port = parsed.port;
|
|
826
|
-
}
|
|
827
|
-
} else if (local) {
|
|
828
|
-
protocol = local.protocol;
|
|
829
|
-
host = local.host;
|
|
830
|
-
port = local.port;
|
|
831
|
-
}
|
|
832
|
-
super({
|
|
833
|
-
protocol: protocol ?? "http",
|
|
834
|
-
host: host ?? "localhost",
|
|
835
|
-
port: port ?? generatePortFromName({ name: appName })
|
|
836
|
-
});
|
|
837
|
-
}
|
|
611
|
+
const applicationsWithoutRouting = Object.entries(
|
|
612
|
+
applicationConfigsById
|
|
613
|
+
).filter(([, app]) => isDefaultApp(app));
|
|
614
|
+
const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
|
|
615
|
+
(acc) => {
|
|
616
|
+
return acc + 1;
|
|
617
|
+
},
|
|
618
|
+
0
|
|
619
|
+
);
|
|
620
|
+
if (numApplicationsWithoutRouting === 0) {
|
|
621
|
+
throw new MicrofrontendError(
|
|
622
|
+
"No default application found. At least one application needs to be the default by omitting routing.",
|
|
623
|
+
{ type: "config", subtype: "no_default_application" }
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
if (numApplicationsWithoutRouting > 1) {
|
|
627
|
+
const applicationNamesMissingRouting = applicationsWithoutRouting.map(
|
|
628
|
+
([name]) => name
|
|
629
|
+
);
|
|
630
|
+
throw new MicrofrontendError(
|
|
631
|
+
`All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
|
|
632
|
+
{ type: "config", subtype: "multiple_default_applications" }
|
|
633
|
+
);
|
|
634
|
+
}
|
|
838
635
|
};
|
|
839
636
|
|
|
840
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
|
|
841
|
-
function generateAutomationBypassEnvVarName({
|
|
842
|
-
name
|
|
843
|
-
}) {
|
|
844
|
-
return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
637
|
// src/config/microfrontends-config/isomorphic/application.ts
|
|
848
638
|
var Application = class {
|
|
849
639
|
constructor(name, {
|
|
@@ -924,9 +714,6 @@ var ChildApplication = class extends Application {
|
|
|
924
714
|
}
|
|
925
715
|
};
|
|
926
716
|
|
|
927
|
-
// src/config/microfrontends-config/isomorphic/constants.ts
|
|
928
|
-
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
929
|
-
|
|
930
717
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
931
718
|
var MicrofrontendConfigIsomorphic = class {
|
|
932
719
|
constructor({
|
|
@@ -970,7 +757,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
970
757
|
};
|
|
971
758
|
}
|
|
972
759
|
static validate(config) {
|
|
973
|
-
const c = typeof config === "string" ?
|
|
760
|
+
const c = typeof config === "string" ? parse(config) : config;
|
|
974
761
|
validateConfigPaths(c.applications);
|
|
975
762
|
validateConfigDefaultApplication(c.applications);
|
|
976
763
|
return c;
|
|
@@ -979,7 +766,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
979
766
|
cookies
|
|
980
767
|
}) {
|
|
981
768
|
return new MicrofrontendConfigIsomorphic({
|
|
982
|
-
config:
|
|
769
|
+
config: parse(getConfigStringFromEnv()),
|
|
983
770
|
overrides: parseOverrides(cookies ?? [])
|
|
984
771
|
});
|
|
985
772
|
}
|
|
@@ -1045,9 +832,17 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1045
832
|
return this.defaultApplication;
|
|
1046
833
|
}
|
|
1047
834
|
/**
|
|
1048
|
-
* Returns the configured port for the local proxy
|
|
835
|
+
* Returns the configured port for the local proxy.
|
|
836
|
+
* Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
|
|
1049
837
|
*/
|
|
1050
838
|
getLocalProxyPort() {
|
|
839
|
+
const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
|
|
840
|
+
if (portOverride) {
|
|
841
|
+
const port = Number.parseInt(portOverride, 10);
|
|
842
|
+
if (!Number.isNaN(port) && port > 0 && port < 65536) {
|
|
843
|
+
return port;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
1051
846
|
return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
|
|
1052
847
|
}
|
|
1053
848
|
toClientConfig(options) {
|
|
@@ -1070,75 +865,394 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1070
865
|
{
|
|
1071
866
|
removeFlaggedPaths: options?.removeFlaggedPaths
|
|
1072
867
|
}
|
|
1073
|
-
);
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Serializes the class back to the Schema type.
|
|
1077
|
-
*
|
|
1078
|
-
* NOTE: This is used when writing the config to disk and must always match the input Schema
|
|
1079
|
-
*/
|
|
1080
|
-
toSchemaJson() {
|
|
1081
|
-
return this.serialized.config;
|
|
1082
|
-
}
|
|
1083
|
-
serialize() {
|
|
1084
|
-
return this.serialized;
|
|
1085
|
-
}
|
|
1086
|
-
};
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Serializes the class back to the Schema type.
|
|
872
|
+
*
|
|
873
|
+
* NOTE: This is used when writing the config to disk and must always match the input Schema
|
|
874
|
+
*/
|
|
875
|
+
toSchemaJson() {
|
|
876
|
+
return this.serialized.config;
|
|
877
|
+
}
|
|
878
|
+
serialize() {
|
|
879
|
+
return this.serialized;
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// src/config/microfrontends/utils/find-config.ts
|
|
884
|
+
import fs from "node:fs";
|
|
885
|
+
import { join } from "node:path";
|
|
886
|
+
|
|
887
|
+
// src/config/microfrontends/utils/get-config-file-name.ts
|
|
888
|
+
var DEFAULT_CONFIGURATION_FILENAMES = [
|
|
889
|
+
"microfrontends.json",
|
|
890
|
+
"microfrontends.jsonc"
|
|
891
|
+
];
|
|
892
|
+
function getPossibleConfigurationFilenames({
|
|
893
|
+
customConfigFilename
|
|
894
|
+
}) {
|
|
895
|
+
if (customConfigFilename) {
|
|
896
|
+
if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
|
|
897
|
+
throw new Error(
|
|
898
|
+
`Found VC_MICROFRONTENDS_CONFIG_FILE_NAME but the name is invalid. Received: ${customConfigFilename}. The file name must end with '.json' or '.jsonc'. It's also possible for the env var to include the path, eg microfrontends-dev.json or /path/to/microfrontends-dev.json.`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
return Array.from(
|
|
902
|
+
/* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return DEFAULT_CONFIGURATION_FILENAMES;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/config/microfrontends/utils/find-config.ts
|
|
909
|
+
function findConfig({
|
|
910
|
+
dir,
|
|
911
|
+
customConfigFilename
|
|
912
|
+
}) {
|
|
913
|
+
for (const filename of getPossibleConfigurationFilenames({
|
|
914
|
+
customConfigFilename
|
|
915
|
+
})) {
|
|
916
|
+
const maybeConfig = join(dir, filename);
|
|
917
|
+
if (fs.existsSync(maybeConfig)) {
|
|
918
|
+
return maybeConfig;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/config/microfrontends/utils/find-package-root.ts
|
|
925
|
+
import fs2 from "node:fs";
|
|
926
|
+
import path from "node:path";
|
|
927
|
+
var PACKAGE_JSON = "package.json";
|
|
928
|
+
function findPackageRoot(startDir) {
|
|
929
|
+
let currentDir = startDir || process.cwd();
|
|
930
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
931
|
+
const pkgJsonPath = path.join(currentDir, PACKAGE_JSON);
|
|
932
|
+
if (fs2.existsSync(pkgJsonPath)) {
|
|
933
|
+
return currentDir;
|
|
934
|
+
}
|
|
935
|
+
currentDir = path.dirname(currentDir);
|
|
936
|
+
}
|
|
937
|
+
throw new Error(
|
|
938
|
+
`The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/config/microfrontends/utils/find-repository-root.ts
|
|
943
|
+
import fs3 from "node:fs";
|
|
944
|
+
import path2 from "node:path";
|
|
945
|
+
var GIT_DIRECTORY = ".git";
|
|
946
|
+
function hasGitDirectory(dir) {
|
|
947
|
+
const gitPath = path2.join(dir, GIT_DIRECTORY);
|
|
948
|
+
return fs3.existsSync(gitPath) && fs3.statSync(gitPath).isDirectory();
|
|
949
|
+
}
|
|
950
|
+
function hasPnpmWorkspaces(dir) {
|
|
951
|
+
return fs3.existsSync(path2.join(dir, "pnpm-workspace.yaml"));
|
|
952
|
+
}
|
|
953
|
+
function hasPackageJson(dir) {
|
|
954
|
+
return fs3.existsSync(path2.join(dir, "package.json"));
|
|
955
|
+
}
|
|
956
|
+
function findRepositoryRoot(startDir) {
|
|
957
|
+
if (process.env.NX_WORKSPACE_ROOT) {
|
|
958
|
+
return process.env.NX_WORKSPACE_ROOT;
|
|
959
|
+
}
|
|
960
|
+
let currentDir = startDir || process.cwd();
|
|
961
|
+
let lastPackageJsonDir = null;
|
|
962
|
+
while (currentDir !== path2.parse(currentDir).root) {
|
|
963
|
+
if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
|
|
964
|
+
return currentDir;
|
|
965
|
+
}
|
|
966
|
+
if (hasPackageJson(currentDir)) {
|
|
967
|
+
lastPackageJsonDir = currentDir;
|
|
968
|
+
}
|
|
969
|
+
currentDir = path2.dirname(currentDir);
|
|
970
|
+
}
|
|
971
|
+
if (lastPackageJsonDir) {
|
|
972
|
+
return lastPackageJsonDir;
|
|
973
|
+
}
|
|
974
|
+
throw new Error(
|
|
975
|
+
`Could not find the root of the repository for ${startDir}. Please ensure that the directory is part of a Git repository. If you suspect that this should work, please file an issue to the Vercel team.`
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/config/microfrontends/utils/get-application-context.ts
|
|
980
|
+
import fs4 from "node:fs";
|
|
981
|
+
import path3 from "node:path";
|
|
982
|
+
function getApplicationContext(opts) {
|
|
983
|
+
if (opts?.appName) {
|
|
984
|
+
logger.debug(
|
|
985
|
+
"[MFE Config] Application name from appName parameter:",
|
|
986
|
+
opts.appName
|
|
987
|
+
);
|
|
988
|
+
return { name: opts.appName };
|
|
989
|
+
}
|
|
990
|
+
if (process.env.VERCEL_PROJECT_NAME) {
|
|
991
|
+
logger.debug(
|
|
992
|
+
"[MFE Config] Application name from VERCEL_PROJECT_NAME:",
|
|
993
|
+
process.env.VERCEL_PROJECT_NAME
|
|
994
|
+
);
|
|
995
|
+
return {
|
|
996
|
+
name: process.env.VERCEL_PROJECT_NAME,
|
|
997
|
+
projectName: process.env.VERCEL_PROJECT_NAME
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
if (process.env.NX_TASK_TARGET_PROJECT) {
|
|
1001
|
+
logger.debug(
|
|
1002
|
+
"[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
|
|
1003
|
+
process.env.NX_TASK_TARGET_PROJECT
|
|
1004
|
+
);
|
|
1005
|
+
return {
|
|
1006
|
+
name: process.env.NX_TASK_TARGET_PROJECT,
|
|
1007
|
+
packageJsonName: process.env.NX_TASK_TARGET_PROJECT
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
try {
|
|
1011
|
+
const vercelProjectJsonPath = fs4.readFileSync(
|
|
1012
|
+
path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
|
|
1013
|
+
"utf-8"
|
|
1014
|
+
);
|
|
1015
|
+
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
1016
|
+
if (projectJson.projectName) {
|
|
1017
|
+
logger.debug(
|
|
1018
|
+
"[MFE Config] Application name from .vercel/project.json:",
|
|
1019
|
+
projectJson.projectName
|
|
1020
|
+
);
|
|
1021
|
+
return {
|
|
1022
|
+
name: projectJson.projectName,
|
|
1023
|
+
projectName: projectJson.projectName
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
} catch (_) {
|
|
1027
|
+
}
|
|
1028
|
+
try {
|
|
1029
|
+
const packageJsonString = fs4.readFileSync(
|
|
1030
|
+
path3.join(opts?.packageRoot || ".", "package.json"),
|
|
1031
|
+
"utf-8"
|
|
1032
|
+
);
|
|
1033
|
+
const packageJson = JSON.parse(packageJsonString);
|
|
1034
|
+
if (!packageJson.name) {
|
|
1035
|
+
throw new MicrofrontendError(
|
|
1036
|
+
`package.json file missing required field "name"`,
|
|
1037
|
+
{
|
|
1038
|
+
type: "packageJson",
|
|
1039
|
+
subtype: "missing_field_name",
|
|
1040
|
+
source: "@vercel/microfrontends/next"
|
|
1041
|
+
}
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
logger.debug(
|
|
1045
|
+
"[MFE Config] Application name from package.json:",
|
|
1046
|
+
packageJson.name
|
|
1047
|
+
);
|
|
1048
|
+
return { name: packageJson.name, packageJsonName: packageJson.name };
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
throw MicrofrontendError.handle(err, {
|
|
1051
|
+
fileName: "package.json"
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// src/config/microfrontends/utils/infer-microfrontends-location.ts
|
|
1057
|
+
import { readFileSync, statSync } from "node:fs";
|
|
1058
|
+
import { dirname, join as join2 } from "node:path";
|
|
1059
|
+
import fg from "fast-glob";
|
|
1060
|
+
import { parse as parse2 } from "jsonc-parser";
|
|
1061
|
+
var configCache = {};
|
|
1062
|
+
function findPackageWithMicrofrontendsConfig({
|
|
1063
|
+
repositoryRoot,
|
|
1064
|
+
applicationContext,
|
|
1065
|
+
customConfigFilename
|
|
1066
|
+
}) {
|
|
1067
|
+
const applicationName = applicationContext.name;
|
|
1068
|
+
logger.debug(
|
|
1069
|
+
"[MFE Config] Searching repository for configs containing application:",
|
|
1070
|
+
applicationName
|
|
1071
|
+
);
|
|
1072
|
+
try {
|
|
1073
|
+
const microfrontendsJsonPaths = fg.globSync(
|
|
1074
|
+
`**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
|
|
1075
|
+
{
|
|
1076
|
+
cwd: repositoryRoot,
|
|
1077
|
+
absolute: true,
|
|
1078
|
+
onlyFiles: true,
|
|
1079
|
+
followSymbolicLinks: false,
|
|
1080
|
+
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
1081
|
+
}
|
|
1082
|
+
);
|
|
1083
|
+
logger.debug(
|
|
1084
|
+
"[MFE Config] Found",
|
|
1085
|
+
microfrontendsJsonPaths.length,
|
|
1086
|
+
"config file(s) in repository"
|
|
1087
|
+
);
|
|
1088
|
+
const matchingPaths = [];
|
|
1089
|
+
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
1090
|
+
if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
|
|
1091
|
+
matchingPaths.push(microfrontendsJsonPath);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
logger.debug(
|
|
1095
|
+
"[MFE Config] Total matching config files:",
|
|
1096
|
+
matchingPaths.length
|
|
1097
|
+
);
|
|
1098
|
+
if (matchingPaths.length > 1) {
|
|
1099
|
+
throw new MicrofrontendError(
|
|
1100
|
+
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
|
|
1101
|
+
${matchingPaths.join("\n \u2022 ")}`,
|
|
1102
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
if (matchingPaths.length === 0) {
|
|
1106
|
+
if (repositoryRoot && doesMisplacedConfigExist(
|
|
1107
|
+
repositoryRoot,
|
|
1108
|
+
applicationName,
|
|
1109
|
+
customConfigFilename
|
|
1110
|
+
)) {
|
|
1111
|
+
logger.debug(
|
|
1112
|
+
"[MFE Config] Found misplaced config in wrong .vercel directory in repository"
|
|
1113
|
+
);
|
|
1114
|
+
const misplacedConfigPath = join2(
|
|
1115
|
+
repositoryRoot,
|
|
1116
|
+
".vercel",
|
|
1117
|
+
customConfigFilename || "microfrontends.json"
|
|
1118
|
+
);
|
|
1119
|
+
throw new MicrofrontendError(
|
|
1120
|
+
`Unable to automatically infer the location of the \`microfrontends.json\` file.
|
|
1121
|
+
|
|
1122
|
+
A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
|
|
1123
|
+
However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
|
|
1124
|
+
|
|
1125
|
+
To fix this:
|
|
1126
|
+
1. If using \`vercel link\`, run it with \`vercel link --repo\` to handle monorepos, or run \`vercel microfrontends pull --cwd=<application-directory>\` to make sure it pulls the \`microfrontends.json\` file to the correct location
|
|
1127
|
+
2. If manually defined, move the config file to the \`.vercel\` directory in your application
|
|
1128
|
+
3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
|
|
1129
|
+
|
|
1130
|
+
For more information, see: https://vercel.com/docs/cli/project-linking`,
|
|
1131
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
let additionalErrorMessage = "";
|
|
1135
|
+
if (microfrontendsJsonPaths.length > 0) {
|
|
1136
|
+
if (!applicationContext.projectName) {
|
|
1137
|
+
additionalErrorMessage = `
|
|
1138
|
+
|
|
1139
|
+
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.`;
|
|
1140
|
+
} else {
|
|
1141
|
+
additionalErrorMessage = `
|
|
1142
|
+
|
|
1143
|
+
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
throw new MicrofrontendError(
|
|
1147
|
+
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
1087
1148
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1149
|
+
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.
|
|
1150
|
+
|
|
1151
|
+
If your Vercel Microfrontends configuration has a custom name, ensure the VC_MICROFRONTENDS_CONFIG_FILE_NAME environment variable is set, you can pull the vercel project environment variables using the "vercel env pull" command.
|
|
1152
|
+
|
|
1153
|
+
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
1154
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
const [packageJsonPath] = matchingPaths;
|
|
1158
|
+
return dirname(packageJsonPath);
|
|
1159
|
+
} catch (error2) {
|
|
1160
|
+
if (error2 instanceof MicrofrontendError) {
|
|
1161
|
+
throw error2;
|
|
1162
|
+
}
|
|
1163
|
+
return null;
|
|
1094
1164
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1165
|
+
}
|
|
1166
|
+
function inferMicrofrontendsLocation(opts) {
|
|
1167
|
+
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
|
|
1168
|
+
if (configCache[cacheKey]) {
|
|
1169
|
+
return configCache[cacheKey];
|
|
1100
1170
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1171
|
+
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
1172
|
+
if (!result) {
|
|
1173
|
+
throw new MicrofrontendError(
|
|
1174
|
+
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
1175
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1176
|
+
);
|
|
1106
1177
|
}
|
|
1178
|
+
configCache[cacheKey] = result;
|
|
1179
|
+
return result;
|
|
1180
|
+
}
|
|
1181
|
+
function existsSync(path6) {
|
|
1107
1182
|
try {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
"utf-8"
|
|
1111
|
-
);
|
|
1112
|
-
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
1113
|
-
if (projectJson.projectName) {
|
|
1114
|
-
return {
|
|
1115
|
-
name: projectJson.projectName,
|
|
1116
|
-
projectName: projectJson.projectName
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1183
|
+
statSync(path6);
|
|
1184
|
+
return true;
|
|
1119
1185
|
} catch (_) {
|
|
1186
|
+
return false;
|
|
1120
1187
|
}
|
|
1188
|
+
}
|
|
1189
|
+
function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
|
|
1190
|
+
logger.debug(
|
|
1191
|
+
"[MFE Config] Looking for misplaced config in wrong .vercel directory"
|
|
1192
|
+
);
|
|
1193
|
+
const misplacedConfigPath = join2(
|
|
1194
|
+
repositoryRoot,
|
|
1195
|
+
".vercel",
|
|
1196
|
+
customConfigFilename || "microfrontends.json"
|
|
1197
|
+
);
|
|
1198
|
+
return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
|
|
1199
|
+
}
|
|
1200
|
+
function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
|
|
1121
1201
|
try {
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1202
|
+
const microfrontendsJsonContent = readFileSync(
|
|
1203
|
+
microfrontendsJsonPath,
|
|
1124
1204
|
"utf-8"
|
|
1125
1205
|
);
|
|
1126
|
-
const
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
type: "packageJson",
|
|
1132
|
-
subtype: "missing_field_name",
|
|
1133
|
-
source: "@vercel/microfrontends/next"
|
|
1134
|
-
}
|
|
1206
|
+
const microfrontendsJson = parse2(microfrontendsJsonContent);
|
|
1207
|
+
if (microfrontendsJson.applications[applicationName]) {
|
|
1208
|
+
logger.debug(
|
|
1209
|
+
"[MFE Config] Found application in config:",
|
|
1210
|
+
microfrontendsJsonPath
|
|
1135
1211
|
);
|
|
1212
|
+
return true;
|
|
1136
1213
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1214
|
+
for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
|
|
1215
|
+
if (app.packageName === applicationName) {
|
|
1216
|
+
logger.debug(
|
|
1217
|
+
"[MFE Config] Found application via packageName in config:",
|
|
1218
|
+
microfrontendsJsonPath
|
|
1219
|
+
);
|
|
1220
|
+
return true;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
} catch (error2) {
|
|
1224
|
+
logger.debug("[MFE Config] Error checking application in config:", error2);
|
|
1225
|
+
}
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/config/microfrontends/utils/is-monorepo.ts
|
|
1230
|
+
import fs5 from "node:fs";
|
|
1231
|
+
import path4 from "node:path";
|
|
1232
|
+
function isMonorepo({
|
|
1233
|
+
repositoryRoot
|
|
1234
|
+
}) {
|
|
1235
|
+
try {
|
|
1236
|
+
if (fs5.existsSync(path4.join(repositoryRoot, "pnpm-workspace.yaml"))) {
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
if (fs5.existsSync(path4.join(repositoryRoot, "vlt-workspaces.json"))) {
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
if (process.env.NX_WORKSPACE_ROOT === path4.resolve(repositoryRoot)) {
|
|
1243
|
+
return true;
|
|
1244
|
+
}
|
|
1245
|
+
const packageJsonPath = path4.join(repositoryRoot, "package.json");
|
|
1246
|
+
if (!fs5.existsSync(packageJsonPath)) {
|
|
1247
|
+
return false;
|
|
1248
|
+
}
|
|
1249
|
+
const packageJson = JSON.parse(
|
|
1250
|
+
fs5.readFileSync(packageJsonPath, "utf-8")
|
|
1251
|
+
);
|
|
1252
|
+
return packageJson.workspaces !== void 0;
|
|
1253
|
+
} catch (error2) {
|
|
1254
|
+
logger.error("Error determining if repository is a monorepo", error2);
|
|
1255
|
+
return false;
|
|
1142
1256
|
}
|
|
1143
1257
|
}
|
|
1144
1258
|
|
|
@@ -1155,8 +1269,8 @@ function getOutputFilePath() {
|
|
|
1155
1269
|
}
|
|
1156
1270
|
|
|
1157
1271
|
// src/config/microfrontends/server/validation.ts
|
|
1158
|
-
import { parse as parse3 } from "jsonc-parser";
|
|
1159
1272
|
import { Ajv } from "ajv";
|
|
1273
|
+
import { parse as parse3 } from "jsonc-parser";
|
|
1160
1274
|
|
|
1161
1275
|
// schema/schema.json
|
|
1162
1276
|
var schema_default = {
|
|
@@ -1184,9 +1298,7 @@ var schema_default = {
|
|
|
1184
1298
|
description: "Optional configuration options for the microfrontend."
|
|
1185
1299
|
}
|
|
1186
1300
|
},
|
|
1187
|
-
required: [
|
|
1188
|
-
"applications"
|
|
1189
|
-
],
|
|
1301
|
+
required: ["applications"],
|
|
1190
1302
|
additionalProperties: false,
|
|
1191
1303
|
description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
|
|
1192
1304
|
},
|
|
@@ -1223,19 +1335,14 @@ var schema_default = {
|
|
|
1223
1335
|
description: "Development configuration for the default application."
|
|
1224
1336
|
}
|
|
1225
1337
|
},
|
|
1226
|
-
required: [
|
|
1227
|
-
"development"
|
|
1228
|
-
],
|
|
1338
|
+
required: ["development"],
|
|
1229
1339
|
additionalProperties: false
|
|
1230
1340
|
},
|
|
1231
1341
|
DefaultDevelopment: {
|
|
1232
1342
|
type: "object",
|
|
1233
1343
|
properties: {
|
|
1234
1344
|
local: {
|
|
1235
|
-
type: [
|
|
1236
|
-
"number",
|
|
1237
|
-
"string"
|
|
1238
|
-
],
|
|
1345
|
+
type: ["number", "string"],
|
|
1239
1346
|
description: "A local port number or host that this application runs on when it is running locally. If passing a string, include the protocol (optional), host (required) and port (optional).\n\nExamples of valid values: 8080, my.localhost.me, my.localhost.me:8080, https://my.localhost.me, https://my.localhost.me:8080.\n\nThe default value is http://localhost:<port> where port is a stable, unique port number (based on the application name).\n\nSee https://vercel.com/docs/microfrontends/local-development."
|
|
1240
1347
|
},
|
|
1241
1348
|
task: {
|
|
@@ -1247,9 +1354,7 @@ var schema_default = {
|
|
|
1247
1354
|
description: "Fallback for local development, could point to any environment. This is required for the default app. This value is used as the fallback for child apps as well if they do not have a fallback.\n\nIf passing a string, include the protocol (optional), host (required) and port (optional). For example: `https://this.ismyhost:8080`. If omitted, the protocol defaults to HTTPS. If omitted, the port defaults to `80` for HTTP and `443` for HTTPS.\n\nSee https://vercel.com/docs/microfrontends/local-development."
|
|
1248
1355
|
}
|
|
1249
1356
|
},
|
|
1250
|
-
required: [
|
|
1251
|
-
"fallback"
|
|
1252
|
-
],
|
|
1357
|
+
required: ["fallback"],
|
|
1253
1358
|
additionalProperties: false
|
|
1254
1359
|
},
|
|
1255
1360
|
ChildApplication: {
|
|
@@ -1272,19 +1377,14 @@ var schema_default = {
|
|
|
1272
1377
|
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.\n\nThe default value is the auto-generated asset prefix of the form `vc-ap-<hash>`.\n\nSee https://vercel.com/docs/microfrontends/path-routing#asset-prefix."
|
|
1273
1378
|
}
|
|
1274
1379
|
},
|
|
1275
|
-
required: [
|
|
1276
|
-
"routing"
|
|
1277
|
-
],
|
|
1380
|
+
required: ["routing"],
|
|
1278
1381
|
additionalProperties: false
|
|
1279
1382
|
},
|
|
1280
1383
|
ChildDevelopment: {
|
|
1281
1384
|
type: "object",
|
|
1282
1385
|
properties: {
|
|
1283
1386
|
local: {
|
|
1284
|
-
type: [
|
|
1285
|
-
"number",
|
|
1286
|
-
"string"
|
|
1287
|
-
],
|
|
1387
|
+
type: ["number", "string"],
|
|
1288
1388
|
description: "A local port number or host that this application runs on when it is running locally. If passing a string, include the protocol (optional), host (required) and port (optional).\n\nExamples of valid values: 8080, my.localhost.me, my.localhost.me:8080, https://my.localhost.me, https://my.localhost.me:8080.\n\nThe default value is http://localhost:<port> where port is a stable, unique port number (based on the application name).\n\nSee https://vercel.com/docs/microfrontends/local-development."
|
|
1289
1389
|
},
|
|
1290
1390
|
task: {
|
|
@@ -1324,9 +1424,7 @@ var schema_default = {
|
|
|
1324
1424
|
description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
|
|
1325
1425
|
}
|
|
1326
1426
|
},
|
|
1327
|
-
required: [
|
|
1328
|
-
"paths"
|
|
1329
|
-
],
|
|
1427
|
+
required: ["paths"],
|
|
1330
1428
|
additionalProperties: false,
|
|
1331
1429
|
description: "A group of paths that is routed to this application."
|
|
1332
1430
|
},
|
|
@@ -1505,7 +1603,13 @@ var MicrofrontendsServer = class {
|
|
|
1505
1603
|
filePath,
|
|
1506
1604
|
cookies
|
|
1507
1605
|
} = {}) {
|
|
1606
|
+
logger.debug("[MFE Config] Starting config inference", {
|
|
1607
|
+
appName,
|
|
1608
|
+
directory: directory || process.cwd(),
|
|
1609
|
+
filePath
|
|
1610
|
+
});
|
|
1508
1611
|
if (filePath) {
|
|
1612
|
+
logger.debug("[MFE Config] Using explicit filePath:", filePath);
|
|
1509
1613
|
return MicrofrontendsServer.fromFile({
|
|
1510
1614
|
filePath,
|
|
1511
1615
|
cookies
|
|
@@ -1513,16 +1617,25 @@ var MicrofrontendsServer = class {
|
|
|
1513
1617
|
}
|
|
1514
1618
|
try {
|
|
1515
1619
|
const packageRoot = findPackageRoot(directory);
|
|
1620
|
+
logger.debug("[MFE Config] Package root:", packageRoot);
|
|
1516
1621
|
const applicationContext = getApplicationContext({
|
|
1517
1622
|
appName,
|
|
1518
1623
|
packageRoot
|
|
1519
1624
|
});
|
|
1625
|
+
logger.debug("[MFE Config] Application context:", applicationContext);
|
|
1520
1626
|
const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
|
|
1627
|
+
if (customConfigFilename) {
|
|
1628
|
+
logger.debug(
|
|
1629
|
+
"[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
|
|
1630
|
+
customConfigFilename
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1521
1633
|
const maybeConfig = findConfig({
|
|
1522
1634
|
dir: packageRoot,
|
|
1523
1635
|
customConfigFilename
|
|
1524
1636
|
});
|
|
1525
1637
|
if (maybeConfig) {
|
|
1638
|
+
logger.debug("[MFE Config] Config found at package root:", maybeConfig);
|
|
1526
1639
|
return MicrofrontendsServer.fromFile({
|
|
1527
1640
|
filePath: maybeConfig,
|
|
1528
1641
|
cookies
|
|
@@ -1530,42 +1643,78 @@ var MicrofrontendsServer = class {
|
|
|
1530
1643
|
}
|
|
1531
1644
|
const repositoryRoot = findRepositoryRoot();
|
|
1532
1645
|
const isMonorepo2 = isMonorepo({ repositoryRoot });
|
|
1646
|
+
logger.debug(
|
|
1647
|
+
"[MFE Config] Repository root:",
|
|
1648
|
+
repositoryRoot,
|
|
1649
|
+
"Is monorepo:",
|
|
1650
|
+
isMonorepo2
|
|
1651
|
+
);
|
|
1533
1652
|
const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
|
|
1534
1653
|
if (typeof configFromEnv === "string") {
|
|
1654
|
+
logger.debug(
|
|
1655
|
+
"[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
|
|
1656
|
+
configFromEnv
|
|
1657
|
+
);
|
|
1535
1658
|
const maybeConfigFromEnv = resolve(packageRoot, configFromEnv);
|
|
1536
1659
|
if (maybeConfigFromEnv) {
|
|
1660
|
+
logger.debug(
|
|
1661
|
+
"[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
|
|
1662
|
+
maybeConfigFromEnv
|
|
1663
|
+
);
|
|
1537
1664
|
return MicrofrontendsServer.fromFile({
|
|
1538
1665
|
filePath: maybeConfigFromEnv,
|
|
1539
1666
|
cookies
|
|
1540
1667
|
});
|
|
1541
1668
|
}
|
|
1542
1669
|
} else {
|
|
1670
|
+
const vercelDir = join3(packageRoot, ".vercel");
|
|
1671
|
+
logger.debug(
|
|
1672
|
+
"[MFE Config] Searching for config in .vercel directory:",
|
|
1673
|
+
vercelDir
|
|
1674
|
+
);
|
|
1543
1675
|
const maybeConfigFromVercel = findConfig({
|
|
1544
|
-
dir:
|
|
1676
|
+
dir: vercelDir,
|
|
1545
1677
|
customConfigFilename
|
|
1546
1678
|
});
|
|
1547
1679
|
if (maybeConfigFromVercel) {
|
|
1680
|
+
logger.debug(
|
|
1681
|
+
"[MFE Config] Config found in .vercel directory:",
|
|
1682
|
+
maybeConfigFromVercel
|
|
1683
|
+
);
|
|
1548
1684
|
return MicrofrontendsServer.fromFile({
|
|
1549
1685
|
filePath: maybeConfigFromVercel,
|
|
1550
1686
|
cookies
|
|
1551
1687
|
});
|
|
1552
1688
|
}
|
|
1553
1689
|
if (isMonorepo2) {
|
|
1690
|
+
logger.debug(
|
|
1691
|
+
"[MFE Config] Inferring microfrontends location in monorepo for application:",
|
|
1692
|
+
applicationContext.name
|
|
1693
|
+
);
|
|
1554
1694
|
const defaultPackage = inferMicrofrontendsLocation({
|
|
1555
1695
|
repositoryRoot,
|
|
1556
1696
|
applicationContext,
|
|
1557
1697
|
customConfigFilename
|
|
1558
1698
|
});
|
|
1699
|
+
logger.debug(
|
|
1700
|
+
"[MFE Config] Inferred package location:",
|
|
1701
|
+
defaultPackage
|
|
1702
|
+
);
|
|
1559
1703
|
const maybeConfigFromDefault = findConfig({
|
|
1560
1704
|
dir: defaultPackage,
|
|
1561
1705
|
customConfigFilename
|
|
1562
1706
|
});
|
|
1563
1707
|
if (maybeConfigFromDefault) {
|
|
1708
|
+
logger.debug(
|
|
1709
|
+
"[MFE Config] Config found in inferred package:",
|
|
1710
|
+
maybeConfigFromDefault
|
|
1711
|
+
);
|
|
1564
1712
|
return MicrofrontendsServer.fromFile({
|
|
1565
1713
|
filePath: maybeConfigFromDefault,
|
|
1566
1714
|
cookies
|
|
1567
1715
|
});
|
|
1568
1716
|
}
|
|
1717
|
+
logger.debug("[MFE Config] No config found in inferred package");
|
|
1569
1718
|
}
|
|
1570
1719
|
}
|
|
1571
1720
|
throw new MicrofrontendError(
|
|
@@ -1591,8 +1740,13 @@ var MicrofrontendsServer = class {
|
|
|
1591
1740
|
cookies
|
|
1592
1741
|
}) {
|
|
1593
1742
|
try {
|
|
1743
|
+
logger.debug("[MFE Config] Reading config from file:", filePath);
|
|
1594
1744
|
const configJson = fs6.readFileSync(filePath, "utf-8");
|
|
1595
1745
|
const config = MicrofrontendsServer.validate(configJson);
|
|
1746
|
+
logger.debug(
|
|
1747
|
+
"[MFE Config] Config loaded with applications:",
|
|
1748
|
+
Object.keys(config.applications)
|
|
1749
|
+
);
|
|
1596
1750
|
return new MicrofrontendsServer({
|
|
1597
1751
|
config,
|
|
1598
1752
|
overrides: cookies ? parseOverrides(cookies) : void 0
|
|
@@ -1632,6 +1786,51 @@ var MicrofrontendsServer = class {
|
|
|
1632
1786
|
}
|
|
1633
1787
|
};
|
|
1634
1788
|
|
|
1789
|
+
// src/next/config/env.ts
|
|
1790
|
+
function debugEnv(env) {
|
|
1791
|
+
const indent = " ".repeat(4);
|
|
1792
|
+
const header = "env (key \u2192 val)";
|
|
1793
|
+
const separator = "\u23AF".repeat(header.length);
|
|
1794
|
+
const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
|
|
1795
|
+
const table = Object.keys(env).map((key, idx) => {
|
|
1796
|
+
const paddedKey = key.padEnd(maxKeyLength);
|
|
1797
|
+
return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
|
|
1798
|
+
}).join("\n");
|
|
1799
|
+
logger.debug(`${indent}${header}
|
|
1800
|
+
${indent}${separator}
|
|
1801
|
+
${table}
|
|
1802
|
+
`);
|
|
1803
|
+
}
|
|
1804
|
+
function setEnvironment({
|
|
1805
|
+
app,
|
|
1806
|
+
microfrontends
|
|
1807
|
+
}) {
|
|
1808
|
+
const clientEnvs = {
|
|
1809
|
+
NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
|
|
1810
|
+
NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
|
|
1811
|
+
NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
|
|
1812
|
+
microfrontends.config.toClientConfig({
|
|
1813
|
+
removeFlaggedPaths: true
|
|
1814
|
+
}).serialize()
|
|
1815
|
+
),
|
|
1816
|
+
...app.getAssetPrefix() ? {
|
|
1817
|
+
NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
|
|
1818
|
+
} : {},
|
|
1819
|
+
...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
|
|
1820
|
+
NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
|
|
1821
|
+
} : {}
|
|
1822
|
+
};
|
|
1823
|
+
const serverEnvs = {
|
|
1824
|
+
MFE_CURRENT_APPLICATION: app.name,
|
|
1825
|
+
MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
|
|
1826
|
+
};
|
|
1827
|
+
const allEnvs = { ...clientEnvs, ...serverEnvs };
|
|
1828
|
+
for (const [key, value] of Object.entries(allEnvs)) {
|
|
1829
|
+
process.env[key] = value;
|
|
1830
|
+
}
|
|
1831
|
+
debugEnv(allEnvs);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1635
1834
|
// src/next/config/transforms/asset-prefix.ts
|
|
1636
1835
|
function transform(args) {
|
|
1637
1836
|
const { next, app } = args;
|
|
@@ -1736,20 +1935,6 @@ ${indent} - Automatically redirecting all requests to local microfrontends proxy
|
|
|
1736
1935
|
return { next };
|
|
1737
1936
|
}
|
|
1738
1937
|
|
|
1739
|
-
// src/next/config/transforms/transpile-packages.ts
|
|
1740
|
-
function transform5(args) {
|
|
1741
|
-
const { next } = args;
|
|
1742
|
-
if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
|
|
1743
|
-
next.transpilePackages = [
|
|
1744
|
-
...next.transpilePackages || [],
|
|
1745
|
-
"@vercel/microfrontends"
|
|
1746
|
-
];
|
|
1747
|
-
}
|
|
1748
|
-
return {
|
|
1749
|
-
next
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
1938
|
// src/next/config/transforms/rewrites.ts
|
|
1754
1939
|
function debugRewrites(rewrites) {
|
|
1755
1940
|
const indent = " ".repeat(4);
|
|
@@ -1778,7 +1963,7 @@ function rewritesMapToArr(rewrites) {
|
|
|
1778
1963
|
];
|
|
1779
1964
|
});
|
|
1780
1965
|
}
|
|
1781
|
-
function
|
|
1966
|
+
function transform5(args) {
|
|
1782
1967
|
const { app, next } = args;
|
|
1783
1968
|
const buildBeforeFiles = () => {
|
|
1784
1969
|
const rewrites = /* @__PURE__ */ new Map();
|
|
@@ -1841,6 +2026,20 @@ function transform6(args) {
|
|
|
1841
2026
|
};
|
|
1842
2027
|
}
|
|
1843
2028
|
|
|
2029
|
+
// src/next/config/transforms/transpile-packages.ts
|
|
2030
|
+
function transform6(args) {
|
|
2031
|
+
const { next } = args;
|
|
2032
|
+
if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
|
|
2033
|
+
next.transpilePackages = [
|
|
2034
|
+
...next.transpilePackages || [],
|
|
2035
|
+
"@vercel/microfrontends"
|
|
2036
|
+
];
|
|
2037
|
+
}
|
|
2038
|
+
return {
|
|
2039
|
+
next
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
|
|
1844
2043
|
// src/next/config/transforms/webpack.ts
|
|
1845
2044
|
import fs7 from "node:fs";
|
|
1846
2045
|
import { createRequire } from "node:module";
|
|
@@ -1873,8 +2072,12 @@ var nextVersion = getNextJsVersion();
|
|
|
1873
2072
|
function transform7(args) {
|
|
1874
2073
|
const useDefineServer = args.opts?.preferWebpackEnvironmentPlugin ? false : semver.gte(nextVersion, "15.4.0-canary.41");
|
|
1875
2074
|
const { next, microfrontend, opts } = args;
|
|
2075
|
+
const isNext16OrHigher = semver.gte(nextVersion, "16.0.0");
|
|
2076
|
+
const hasTurbopackConfig = Boolean(next.turbopack);
|
|
2077
|
+
const turbopackConfig = isNext16OrHigher && !hasTurbopackConfig ? { turbopack: {} } : {};
|
|
1876
2078
|
const configWithWebpack = {
|
|
1877
2079
|
...next,
|
|
2080
|
+
...turbopackConfig,
|
|
1878
2081
|
...useDefineServer ? {
|
|
1879
2082
|
compiler: {
|
|
1880
2083
|
...next.compiler,
|
|
@@ -1954,56 +2157,11 @@ var transforms = {
|
|
|
1954
2157
|
buildId: transform2,
|
|
1955
2158
|
draftMode: transform3,
|
|
1956
2159
|
redirects: transform4,
|
|
1957
|
-
rewrites:
|
|
1958
|
-
transpilePackages:
|
|
2160
|
+
rewrites: transform5,
|
|
2161
|
+
transpilePackages: transform6,
|
|
1959
2162
|
webpack: transform7
|
|
1960
2163
|
};
|
|
1961
2164
|
|
|
1962
|
-
// src/next/config/env.ts
|
|
1963
|
-
function debugEnv(env) {
|
|
1964
|
-
const indent = " ".repeat(4);
|
|
1965
|
-
const header = "env (key \u2192 val)";
|
|
1966
|
-
const separator = "\u23AF".repeat(header.length);
|
|
1967
|
-
const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
|
|
1968
|
-
const table = Object.keys(env).map((key, idx) => {
|
|
1969
|
-
const paddedKey = key.padEnd(maxKeyLength);
|
|
1970
|
-
return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
|
|
1971
|
-
}).join("\n");
|
|
1972
|
-
logger.debug(`${indent}${header}
|
|
1973
|
-
${indent}${separator}
|
|
1974
|
-
${table}
|
|
1975
|
-
`);
|
|
1976
|
-
}
|
|
1977
|
-
function setEnvironment({
|
|
1978
|
-
app,
|
|
1979
|
-
microfrontends
|
|
1980
|
-
}) {
|
|
1981
|
-
const clientEnvs = {
|
|
1982
|
-
NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
|
|
1983
|
-
NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
|
|
1984
|
-
NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
|
|
1985
|
-
microfrontends.config.toClientConfig({
|
|
1986
|
-
removeFlaggedPaths: true
|
|
1987
|
-
}).serialize()
|
|
1988
|
-
),
|
|
1989
|
-
...app.getAssetPrefix() ? {
|
|
1990
|
-
NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
|
|
1991
|
-
} : {},
|
|
1992
|
-
...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
|
|
1993
|
-
NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
|
|
1994
|
-
} : {}
|
|
1995
|
-
};
|
|
1996
|
-
const serverEnvs = {
|
|
1997
|
-
MFE_CURRENT_APPLICATION: app.name,
|
|
1998
|
-
MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
|
|
1999
|
-
};
|
|
2000
|
-
const allEnvs = { ...clientEnvs, ...serverEnvs };
|
|
2001
|
-
for (const [key, value] of Object.entries(allEnvs)) {
|
|
2002
|
-
process.env[key] = value;
|
|
2003
|
-
}
|
|
2004
|
-
debugEnv(allEnvs);
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
2165
|
// src/next/config/index.ts
|
|
2008
2166
|
function typedEntries(obj) {
|
|
2009
2167
|
return Object.entries(obj);
|