@vercel/microfrontends 2.2.1 → 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 +19 -0
- package/cli/index.cjs +0 -1
- package/dist/bin/cli.cjs +483 -394
- 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 +583 -511
- package/dist/experimental/sveltekit.cjs.map +1 -1
- package/dist/experimental/sveltekit.js +589 -517
- package/dist/experimental/sveltekit.js.map +1 -1
- package/dist/experimental/vite.cjs +605 -533
- package/dist/experimental/vite.cjs.map +1 -1
- package/dist/experimental/vite.js +614 -542
- package/dist/experimental/vite.js.map +1 -1
- package/dist/microfrontends/server.cjs +601 -529
- package/dist/microfrontends/server.cjs.map +1 -1
- package/dist/microfrontends/server.d.ts +2 -2
- package/dist/microfrontends/server.js +607 -535
- package/dist/microfrontends/server.js.map +1 -1
- package/dist/microfrontends/utils.cjs +101 -50
- package/dist/microfrontends/utils.cjs.map +1 -1
- package/dist/microfrontends/utils.d.ts +4 -4
- package/dist/microfrontends/utils.js +102 -51
- 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 +723 -647
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js +720 -644
- 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 +620 -533
- package/dist/utils/mfe-port.cjs.map +1 -1
- package/dist/utils/mfe-port.d.ts +9 -1
- package/dist/utils/mfe-port.js +632 -546
- 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,274 +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
|
-
|
|
233
|
-
// src/config/microfrontends/utils/get-config-file-name.ts
|
|
234
|
-
var DEFAULT_CONFIGURATION_FILENAMES = [
|
|
235
|
-
"microfrontends.json",
|
|
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
142
|
|
|
254
|
-
// src/config/
|
|
255
|
-
var
|
|
256
|
-
|
|
257
|
-
repositoryRoot,
|
|
258
|
-
applicationContext,
|
|
259
|
-
customConfigFilename
|
|
260
|
-
}) {
|
|
261
|
-
const applicationName = applicationContext.name;
|
|
262
|
-
logger.debug(
|
|
263
|
-
"[MFE Config] Searching repository for configs containing application:",
|
|
264
|
-
applicationName
|
|
265
|
-
);
|
|
266
|
-
try {
|
|
267
|
-
const microfrontendsJsonPaths = fg.globSync(
|
|
268
|
-
`**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
|
|
269
|
-
{
|
|
270
|
-
cwd: repositoryRoot,
|
|
271
|
-
absolute: true,
|
|
272
|
-
onlyFiles: true,
|
|
273
|
-
followSymbolicLinks: false,
|
|
274
|
-
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
275
|
-
}
|
|
276
|
-
);
|
|
277
|
-
logger.debug(
|
|
278
|
-
"[MFE Config] Found",
|
|
279
|
-
microfrontendsJsonPaths.length,
|
|
280
|
-
"config file(s) in repository"
|
|
281
|
-
);
|
|
282
|
-
const matchingPaths = [];
|
|
283
|
-
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
284
|
-
try {
|
|
285
|
-
const microfrontendsJsonContent = readFileSync(
|
|
286
|
-
microfrontendsJsonPath,
|
|
287
|
-
"utf-8"
|
|
288
|
-
);
|
|
289
|
-
const microfrontendsJson = parse(microfrontendsJsonContent);
|
|
290
|
-
if (microfrontendsJson.applications[applicationName]) {
|
|
291
|
-
logger.debug(
|
|
292
|
-
"[MFE Config] Found application in config:",
|
|
293
|
-
microfrontendsJsonPath
|
|
294
|
-
);
|
|
295
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
296
|
-
} else {
|
|
297
|
-
for (const [_, app] of Object.entries(
|
|
298
|
-
microfrontendsJson.applications
|
|
299
|
-
)) {
|
|
300
|
-
if (app.packageName === applicationName) {
|
|
301
|
-
logger.debug(
|
|
302
|
-
"[MFE Config] Found application via packageName in config:",
|
|
303
|
-
microfrontendsJsonPath
|
|
304
|
-
);
|
|
305
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
} catch (error2) {
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
logger.debug(
|
|
313
|
-
"[MFE Config] Total matching config files:",
|
|
314
|
-
matchingPaths.length
|
|
315
|
-
);
|
|
316
|
-
if (matchingPaths.length > 1) {
|
|
317
|
-
throw new MicrofrontendError(
|
|
318
|
-
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
|
|
319
|
-
${matchingPaths.join("\n \u2022 ")}`,
|
|
320
|
-
{ type: "config", subtype: "inference_failed" }
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
if (matchingPaths.length === 0) {
|
|
324
|
-
let additionalErrorMessage = "";
|
|
325
|
-
if (microfrontendsJsonPaths.length > 0) {
|
|
326
|
-
if (!applicationContext.projectName) {
|
|
327
|
-
additionalErrorMessage = `
|
|
328
|
-
|
|
329
|
-
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.`;
|
|
330
|
-
} else {
|
|
331
|
-
additionalErrorMessage = `
|
|
332
|
-
|
|
333
|
-
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
throw new MicrofrontendError(
|
|
337
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
338
|
-
|
|
339
|
-
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.
|
|
340
|
-
|
|
341
|
-
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:`;
|
|
342
146
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
const [packageJsonPath] = matchingPaths;
|
|
348
|
-
return dirname(packageJsonPath);
|
|
349
|
-
} catch (error2) {
|
|
350
|
-
if (error2 instanceof MicrofrontendError) {
|
|
351
|
-
throw error2;
|
|
352
|
-
}
|
|
353
|
-
return null;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function inferMicrofrontendsLocation(opts) {
|
|
357
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
|
|
358
|
-
if (configCache[cacheKey]) {
|
|
359
|
-
return configCache[cacheKey];
|
|
360
|
-
}
|
|
361
|
-
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
362
|
-
if (!result) {
|
|
363
|
-
throw new MicrofrontendError(
|
|
364
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
365
|
-
{ type: "config", subtype: "inference_failed" }
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
configCache[cacheKey] = result;
|
|
369
|
-
return result;
|
|
147
|
+
// src/config/overrides/is-override-cookie.ts
|
|
148
|
+
function isOverrideCookie(cookie) {
|
|
149
|
+
return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
|
|
370
150
|
}
|
|
371
151
|
|
|
372
|
-
// src/config/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
|
-
if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
|
|
383
|
-
return true;
|
|
384
|
-
}
|
|
385
|
-
if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
const packageJsonPath = path2.join(repositoryRoot, "package.json");
|
|
389
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
390
|
-
return false;
|
|
391
|
-
}
|
|
392
|
-
const packageJson = JSON.parse(
|
|
393
|
-
fs2.readFileSync(packageJsonPath, "utf-8")
|
|
394
|
-
);
|
|
395
|
-
return packageJson.workspaces !== void 0;
|
|
396
|
-
} catch (error2) {
|
|
397
|
-
logger.error("Error determining if repository is a monorepo", error2);
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
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
|
+
};
|
|
400
160
|
}
|
|
401
161
|
|
|
402
|
-
// src/config/
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
throw new Error(
|
|
416
|
-
`The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
|
|
417
|
-
);
|
|
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;
|
|
418
174
|
}
|
|
419
175
|
|
|
420
|
-
// src/config/
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
function findConfig({
|
|
424
|
-
dir,
|
|
425
|
-
customConfigFilename
|
|
426
|
-
}) {
|
|
427
|
-
for (const filename of getPossibleConfigurationFilenames({
|
|
428
|
-
customConfigFilename
|
|
429
|
-
})) {
|
|
430
|
-
const maybeConfig = join(dir, filename);
|
|
431
|
-
if (fs4.existsSync(maybeConfig)) {
|
|
432
|
-
return maybeConfig;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
return null;
|
|
176
|
+
// src/config/schema/utils/is-default-app.ts
|
|
177
|
+
function isDefaultApp(a) {
|
|
178
|
+
return !("routing" in a);
|
|
436
179
|
}
|
|
437
180
|
|
|
438
|
-
// src/config/microfrontends-config/isomorphic/index.ts
|
|
439
|
-
import { parse as parse2 } from "jsonc-parser";
|
|
440
|
-
|
|
441
181
|
// src/config/microfrontends-config/client/index.ts
|
|
442
182
|
import { pathToRegexp } from "path-to-regexp";
|
|
443
183
|
var regexpCache = /* @__PURE__ */ new Map();
|
|
@@ -498,46 +238,223 @@ var MicrofrontendConfigClient = class {
|
|
|
498
238
|
}
|
|
499
239
|
return new MicrofrontendConfigClient(JSON.parse(config));
|
|
500
240
|
}
|
|
501
|
-
isEqual(other) {
|
|
502
|
-
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";
|
|
503
375
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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);
|
|
383
|
+
}
|
|
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;
|
|
523
400
|
}
|
|
524
401
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (
|
|
529
|
-
|
|
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;
|
|
530
420
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
421
|
+
super({
|
|
422
|
+
protocol: protocol ?? "http",
|
|
423
|
+
host: host ?? "localhost",
|
|
424
|
+
port: port ?? generatePortFromName({ name: appName })
|
|
425
|
+
});
|
|
536
426
|
}
|
|
537
427
|
};
|
|
538
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
|
+
|
|
539
456
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
540
|
-
import {
|
|
457
|
+
import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
|
|
541
458
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
542
459
|
style: "long",
|
|
543
460
|
type: "conjunction"
|
|
@@ -696,175 +613,27 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
696
613
|
).filter(([, app]) => isDefaultApp(app));
|
|
697
614
|
const numApplicationsWithoutRouting = applicationsWithoutRouting.reduce(
|
|
698
615
|
(acc) => {
|
|
699
|
-
return acc + 1;
|
|
700
|
-
},
|
|
701
|
-
0
|
|
702
|
-
);
|
|
703
|
-
if (numApplicationsWithoutRouting === 0) {
|
|
704
|
-
throw new MicrofrontendError(
|
|
705
|
-
"No default application found. At least one application needs to be the default by omitting routing.",
|
|
706
|
-
{ type: "config", subtype: "no_default_application" }
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
if (numApplicationsWithoutRouting > 1) {
|
|
710
|
-
const applicationNamesMissingRouting = applicationsWithoutRouting.map(
|
|
711
|
-
([name]) => name
|
|
712
|
-
);
|
|
713
|
-
throw new MicrofrontendError(
|
|
714
|
-
`All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
|
|
715
|
-
{ type: "config", subtype: "multiple_default_applications" }
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
721
|
-
import md5 from "md5";
|
|
722
|
-
function hashApplicationName(name) {
|
|
723
|
-
if (!name) {
|
|
724
|
-
throw new Error("Application name is required to generate hash");
|
|
725
|
-
}
|
|
726
|
-
return md5(name).substring(0, 6).padStart(6, "0");
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
730
|
-
var PREFIX = "vc-ap";
|
|
731
|
-
function generateAssetPrefixFromName({
|
|
732
|
-
name
|
|
733
|
-
}) {
|
|
734
|
-
if (!name) {
|
|
735
|
-
throw new Error("Name is required to generate an asset prefix");
|
|
736
|
-
}
|
|
737
|
-
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
741
|
-
function generatePortFromName({
|
|
742
|
-
name,
|
|
743
|
-
minPort = 3e3,
|
|
744
|
-
maxPort = 8e3
|
|
745
|
-
}) {
|
|
746
|
-
if (!name) {
|
|
747
|
-
throw new Error("Name is required to generate a port");
|
|
748
|
-
}
|
|
749
|
-
let hash = 0;
|
|
750
|
-
for (let i = 0; i < name.length; i++) {
|
|
751
|
-
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
752
|
-
hash |= 0;
|
|
753
|
-
}
|
|
754
|
-
hash = Math.abs(hash);
|
|
755
|
-
const range = maxPort - minPort;
|
|
756
|
-
const port = minPort + hash % range;
|
|
757
|
-
return port;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// src/config/microfrontends-config/isomorphic/host.ts
|
|
761
|
-
var Host = class {
|
|
762
|
-
constructor(hostConfig, options) {
|
|
763
|
-
if (typeof hostConfig === "string") {
|
|
764
|
-
({
|
|
765
|
-
protocol: this.protocol,
|
|
766
|
-
host: this.host,
|
|
767
|
-
port: this.port
|
|
768
|
-
} = Host.parseUrl(hostConfig));
|
|
769
|
-
} else {
|
|
770
|
-
const { protocol = "https", host, port } = hostConfig;
|
|
771
|
-
this.protocol = protocol;
|
|
772
|
-
this.host = host;
|
|
773
|
-
this.port = port;
|
|
774
|
-
}
|
|
775
|
-
this.local = options?.isLocal;
|
|
776
|
-
}
|
|
777
|
-
static parseUrl(url, defaultProtocol = "https") {
|
|
778
|
-
let hostToParse = url;
|
|
779
|
-
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
780
|
-
hostToParse = `${defaultProtocol}://${hostToParse}`;
|
|
781
|
-
}
|
|
782
|
-
const parsed = new URL(hostToParse);
|
|
783
|
-
if (!parsed.hostname) {
|
|
784
|
-
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
785
|
-
}
|
|
786
|
-
if (parsed.hash) {
|
|
787
|
-
throw new Error(
|
|
788
|
-
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
789
|
-
);
|
|
790
|
-
}
|
|
791
|
-
if (parsed.username || parsed.password) {
|
|
792
|
-
throw new Error(
|
|
793
|
-
Host.getMicrofrontendsError(
|
|
794
|
-
url,
|
|
795
|
-
"cannot have authentication credentials (username and/or password)"
|
|
796
|
-
)
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
if (parsed.pathname !== "/") {
|
|
800
|
-
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
801
|
-
}
|
|
802
|
-
if (parsed.search) {
|
|
803
|
-
throw new Error(
|
|
804
|
-
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
const protocol = parsed.protocol.slice(0, -1);
|
|
808
|
-
return {
|
|
809
|
-
protocol,
|
|
810
|
-
host: parsed.hostname,
|
|
811
|
-
port: parsed.port ? Number.parseInt(parsed.port) : void 0
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
static getMicrofrontendsError(url, message) {
|
|
815
|
-
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
816
|
-
}
|
|
817
|
-
isLocal() {
|
|
818
|
-
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
819
|
-
}
|
|
820
|
-
toString() {
|
|
821
|
-
const url = this.toUrl();
|
|
822
|
-
return url.toString().replace(/\/$/, "");
|
|
823
|
-
}
|
|
824
|
-
toUrl() {
|
|
825
|
-
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
826
|
-
return new URL(url);
|
|
827
|
-
}
|
|
828
|
-
};
|
|
829
|
-
var LocalHost = class extends Host {
|
|
830
|
-
constructor({
|
|
831
|
-
appName,
|
|
832
|
-
local
|
|
833
|
-
}) {
|
|
834
|
-
let protocol;
|
|
835
|
-
let host;
|
|
836
|
-
let port;
|
|
837
|
-
if (typeof local === "number") {
|
|
838
|
-
port = local;
|
|
839
|
-
} else if (typeof local === "string") {
|
|
840
|
-
if (/^\d+$/.test(local)) {
|
|
841
|
-
port = Number.parseInt(local);
|
|
842
|
-
} else {
|
|
843
|
-
const parsed = Host.parseUrl(local, "http");
|
|
844
|
-
protocol = parsed.protocol;
|
|
845
|
-
host = parsed.host;
|
|
846
|
-
port = parsed.port;
|
|
847
|
-
}
|
|
848
|
-
} else if (local) {
|
|
849
|
-
protocol = local.protocol;
|
|
850
|
-
host = local.host;
|
|
851
|
-
port = local.port;
|
|
852
|
-
}
|
|
853
|
-
super({
|
|
854
|
-
protocol: protocol ?? "http",
|
|
855
|
-
host: host ?? "localhost",
|
|
856
|
-
port: port ?? generatePortFromName({ name: appName })
|
|
857
|
-
});
|
|
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
|
+
);
|
|
858
634
|
}
|
|
859
635
|
};
|
|
860
636
|
|
|
861
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
|
|
862
|
-
function generateAutomationBypassEnvVarName({
|
|
863
|
-
name
|
|
864
|
-
}) {
|
|
865
|
-
return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
637
|
// src/config/microfrontends-config/isomorphic/application.ts
|
|
869
638
|
var Application = class {
|
|
870
639
|
constructor(name, {
|
|
@@ -945,9 +714,6 @@ var ChildApplication = class extends Application {
|
|
|
945
714
|
}
|
|
946
715
|
};
|
|
947
716
|
|
|
948
|
-
// src/config/microfrontends-config/isomorphic/constants.ts
|
|
949
|
-
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
950
|
-
|
|
951
717
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
952
718
|
var MicrofrontendConfigIsomorphic = class {
|
|
953
719
|
constructor({
|
|
@@ -991,7 +757,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
991
757
|
};
|
|
992
758
|
}
|
|
993
759
|
static validate(config) {
|
|
994
|
-
const c = typeof config === "string" ?
|
|
760
|
+
const c = typeof config === "string" ? parse(config) : config;
|
|
995
761
|
validateConfigPaths(c.applications);
|
|
996
762
|
validateConfigDefaultApplication(c.applications);
|
|
997
763
|
return c;
|
|
@@ -1000,7 +766,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1000
766
|
cookies
|
|
1001
767
|
}) {
|
|
1002
768
|
return new MicrofrontendConfigIsomorphic({
|
|
1003
|
-
config:
|
|
769
|
+
config: parse(getConfigStringFromEnv()),
|
|
1004
770
|
overrides: parseOverrides(cookies ?? [])
|
|
1005
771
|
});
|
|
1006
772
|
}
|
|
@@ -1066,9 +832,17 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1066
832
|
return this.defaultApplication;
|
|
1067
833
|
}
|
|
1068
834
|
/**
|
|
1069
|
-
* 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.
|
|
1070
837
|
*/
|
|
1071
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
|
+
}
|
|
1072
846
|
return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
|
|
1073
847
|
}
|
|
1074
848
|
toClientConfig(options) {
|
|
@@ -1091,80 +865,394 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1091
865
|
{
|
|
1092
866
|
removeFlaggedPaths: options?.removeFlaggedPaths
|
|
1093
867
|
}
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Serializes the class back to the Schema type.
|
|
1098
|
-
*
|
|
1099
|
-
* NOTE: This is used when writing the config to disk and must always match the input Schema
|
|
1100
|
-
*/
|
|
1101
|
-
toSchemaJson() {
|
|
1102
|
-
return this.serialized.config;
|
|
1103
|
-
}
|
|
1104
|
-
serialize() {
|
|
1105
|
-
return this.serialized;
|
|
1106
|
-
}
|
|
1107
|
-
};
|
|
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}
|
|
1108
1148
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
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;
|
|
1116
1164
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
};
|
|
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];
|
|
1123
1170
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
name
|
|
1128
|
-
|
|
1129
|
-
|
|
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
|
+
);
|
|
1130
1177
|
}
|
|
1178
|
+
configCache[cacheKey] = result;
|
|
1179
|
+
return result;
|
|
1180
|
+
}
|
|
1181
|
+
function existsSync(path6) {
|
|
1131
1182
|
try {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
"utf-8"
|
|
1135
|
-
);
|
|
1136
|
-
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
1137
|
-
if (projectJson.projectName) {
|
|
1138
|
-
logger.debug("[MFE Config] Application name from .vercel/project.json:", projectJson.projectName);
|
|
1139
|
-
return {
|
|
1140
|
-
name: projectJson.projectName,
|
|
1141
|
-
projectName: projectJson.projectName
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1183
|
+
statSync(path6);
|
|
1184
|
+
return true;
|
|
1144
1185
|
} catch (_) {
|
|
1186
|
+
return false;
|
|
1145
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) {
|
|
1146
1201
|
try {
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1202
|
+
const microfrontendsJsonContent = readFileSync(
|
|
1203
|
+
microfrontendsJsonPath,
|
|
1149
1204
|
"utf-8"
|
|
1150
1205
|
);
|
|
1151
|
-
const
|
|
1152
|
-
if (
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
type: "packageJson",
|
|
1157
|
-
subtype: "missing_field_name",
|
|
1158
|
-
source: "@vercel/microfrontends/next"
|
|
1159
|
-
}
|
|
1206
|
+
const microfrontendsJson = parse2(microfrontendsJsonContent);
|
|
1207
|
+
if (microfrontendsJson.applications[applicationName]) {
|
|
1208
|
+
logger.debug(
|
|
1209
|
+
"[MFE Config] Found application in config:",
|
|
1210
|
+
microfrontendsJsonPath
|
|
1160
1211
|
);
|
|
1212
|
+
return true;
|
|
1161
1213
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
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;
|
|
1168
1256
|
}
|
|
1169
1257
|
}
|
|
1170
1258
|
|
|
@@ -1181,8 +1269,8 @@ function getOutputFilePath() {
|
|
|
1181
1269
|
}
|
|
1182
1270
|
|
|
1183
1271
|
// src/config/microfrontends/server/validation.ts
|
|
1184
|
-
import { parse as parse3 } from "jsonc-parser";
|
|
1185
1272
|
import { Ajv } from "ajv";
|
|
1273
|
+
import { parse as parse3 } from "jsonc-parser";
|
|
1186
1274
|
|
|
1187
1275
|
// schema/schema.json
|
|
1188
1276
|
var schema_default = {
|
|
@@ -1210,9 +1298,7 @@ var schema_default = {
|
|
|
1210
1298
|
description: "Optional configuration options for the microfrontend."
|
|
1211
1299
|
}
|
|
1212
1300
|
},
|
|
1213
|
-
required: [
|
|
1214
|
-
"applications"
|
|
1215
|
-
],
|
|
1301
|
+
required: ["applications"],
|
|
1216
1302
|
additionalProperties: false,
|
|
1217
1303
|
description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
|
|
1218
1304
|
},
|
|
@@ -1249,19 +1335,14 @@ var schema_default = {
|
|
|
1249
1335
|
description: "Development configuration for the default application."
|
|
1250
1336
|
}
|
|
1251
1337
|
},
|
|
1252
|
-
required: [
|
|
1253
|
-
"development"
|
|
1254
|
-
],
|
|
1338
|
+
required: ["development"],
|
|
1255
1339
|
additionalProperties: false
|
|
1256
1340
|
},
|
|
1257
1341
|
DefaultDevelopment: {
|
|
1258
1342
|
type: "object",
|
|
1259
1343
|
properties: {
|
|
1260
1344
|
local: {
|
|
1261
|
-
type: [
|
|
1262
|
-
"number",
|
|
1263
|
-
"string"
|
|
1264
|
-
],
|
|
1345
|
+
type: ["number", "string"],
|
|
1265
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."
|
|
1266
1347
|
},
|
|
1267
1348
|
task: {
|
|
@@ -1273,9 +1354,7 @@ var schema_default = {
|
|
|
1273
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."
|
|
1274
1355
|
}
|
|
1275
1356
|
},
|
|
1276
|
-
required: [
|
|
1277
|
-
"fallback"
|
|
1278
|
-
],
|
|
1357
|
+
required: ["fallback"],
|
|
1279
1358
|
additionalProperties: false
|
|
1280
1359
|
},
|
|
1281
1360
|
ChildApplication: {
|
|
@@ -1298,19 +1377,14 @@ var schema_default = {
|
|
|
1298
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."
|
|
1299
1378
|
}
|
|
1300
1379
|
},
|
|
1301
|
-
required: [
|
|
1302
|
-
"routing"
|
|
1303
|
-
],
|
|
1380
|
+
required: ["routing"],
|
|
1304
1381
|
additionalProperties: false
|
|
1305
1382
|
},
|
|
1306
1383
|
ChildDevelopment: {
|
|
1307
1384
|
type: "object",
|
|
1308
1385
|
properties: {
|
|
1309
1386
|
local: {
|
|
1310
|
-
type: [
|
|
1311
|
-
"number",
|
|
1312
|
-
"string"
|
|
1313
|
-
],
|
|
1387
|
+
type: ["number", "string"],
|
|
1314
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."
|
|
1315
1389
|
},
|
|
1316
1390
|
task: {
|
|
@@ -1350,9 +1424,7 @@ var schema_default = {
|
|
|
1350
1424
|
description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
|
|
1351
1425
|
}
|
|
1352
1426
|
},
|
|
1353
|
-
required: [
|
|
1354
|
-
"paths"
|
|
1355
|
-
],
|
|
1427
|
+
required: ["paths"],
|
|
1356
1428
|
additionalProperties: false,
|
|
1357
1429
|
description: "A group of paths that is routed to this application."
|
|
1358
1430
|
},
|
|
@@ -1595,7 +1667,7 @@ var MicrofrontendsServer = class {
|
|
|
1595
1667
|
});
|
|
1596
1668
|
}
|
|
1597
1669
|
} else {
|
|
1598
|
-
const vercelDir =
|
|
1670
|
+
const vercelDir = join3(packageRoot, ".vercel");
|
|
1599
1671
|
logger.debug(
|
|
1600
1672
|
"[MFE Config] Searching for config in .vercel directory:",
|
|
1601
1673
|
vercelDir
|
|
@@ -1714,6 +1786,51 @@ var MicrofrontendsServer = class {
|
|
|
1714
1786
|
}
|
|
1715
1787
|
};
|
|
1716
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
|
+
|
|
1717
1834
|
// src/next/config/transforms/asset-prefix.ts
|
|
1718
1835
|
function transform(args) {
|
|
1719
1836
|
const { next, app } = args;
|
|
@@ -1818,20 +1935,6 @@ ${indent} - Automatically redirecting all requests to local microfrontends proxy
|
|
|
1818
1935
|
return { next };
|
|
1819
1936
|
}
|
|
1820
1937
|
|
|
1821
|
-
// src/next/config/transforms/transpile-packages.ts
|
|
1822
|
-
function transform5(args) {
|
|
1823
|
-
const { next } = args;
|
|
1824
|
-
if (next.transpilePackages === void 0 || !next.transpilePackages.includes("@vercel/microfrontends")) {
|
|
1825
|
-
next.transpilePackages = [
|
|
1826
|
-
...next.transpilePackages || [],
|
|
1827
|
-
"@vercel/microfrontends"
|
|
1828
|
-
];
|
|
1829
|
-
}
|
|
1830
|
-
return {
|
|
1831
|
-
next
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
1938
|
// src/next/config/transforms/rewrites.ts
|
|
1836
1939
|
function debugRewrites(rewrites) {
|
|
1837
1940
|
const indent = " ".repeat(4);
|
|
@@ -1860,7 +1963,7 @@ function rewritesMapToArr(rewrites) {
|
|
|
1860
1963
|
];
|
|
1861
1964
|
});
|
|
1862
1965
|
}
|
|
1863
|
-
function
|
|
1966
|
+
function transform5(args) {
|
|
1864
1967
|
const { app, next } = args;
|
|
1865
1968
|
const buildBeforeFiles = () => {
|
|
1866
1969
|
const rewrites = /* @__PURE__ */ new Map();
|
|
@@ -1923,6 +2026,20 @@ function transform6(args) {
|
|
|
1923
2026
|
};
|
|
1924
2027
|
}
|
|
1925
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
|
+
|
|
1926
2043
|
// src/next/config/transforms/webpack.ts
|
|
1927
2044
|
import fs7 from "node:fs";
|
|
1928
2045
|
import { createRequire } from "node:module";
|
|
@@ -1955,8 +2072,12 @@ var nextVersion = getNextJsVersion();
|
|
|
1955
2072
|
function transform7(args) {
|
|
1956
2073
|
const useDefineServer = args.opts?.preferWebpackEnvironmentPlugin ? false : semver.gte(nextVersion, "15.4.0-canary.41");
|
|
1957
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: {} } : {};
|
|
1958
2078
|
const configWithWebpack = {
|
|
1959
2079
|
...next,
|
|
2080
|
+
...turbopackConfig,
|
|
1960
2081
|
...useDefineServer ? {
|
|
1961
2082
|
compiler: {
|
|
1962
2083
|
...next.compiler,
|
|
@@ -2036,56 +2157,11 @@ var transforms = {
|
|
|
2036
2157
|
buildId: transform2,
|
|
2037
2158
|
draftMode: transform3,
|
|
2038
2159
|
redirects: transform4,
|
|
2039
|
-
rewrites:
|
|
2040
|
-
transpilePackages:
|
|
2160
|
+
rewrites: transform5,
|
|
2161
|
+
transpilePackages: transform6,
|
|
2041
2162
|
webpack: transform7
|
|
2042
2163
|
};
|
|
2043
2164
|
|
|
2044
|
-
// src/next/config/env.ts
|
|
2045
|
-
function debugEnv(env) {
|
|
2046
|
-
const indent = " ".repeat(4);
|
|
2047
|
-
const header = "env (key \u2192 val)";
|
|
2048
|
-
const separator = "\u23AF".repeat(header.length);
|
|
2049
|
-
const maxKeyLength = Math.max(...Object.keys(env).map((key) => key.length));
|
|
2050
|
-
const table = Object.keys(env).map((key, idx) => {
|
|
2051
|
-
const paddedKey = key.padEnd(maxKeyLength);
|
|
2052
|
-
return `${indent} ${idx + 1}. ${paddedKey} = ${env[key]}`;
|
|
2053
|
-
}).join("\n");
|
|
2054
|
-
logger.debug(`${indent}${header}
|
|
2055
|
-
${indent}${separator}
|
|
2056
|
-
${table}
|
|
2057
|
-
`);
|
|
2058
|
-
}
|
|
2059
|
-
function setEnvironment({
|
|
2060
|
-
app,
|
|
2061
|
-
microfrontends
|
|
2062
|
-
}) {
|
|
2063
|
-
const clientEnvs = {
|
|
2064
|
-
NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
|
|
2065
|
-
NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH: hashApplicationName(app.name),
|
|
2066
|
-
NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
|
|
2067
|
-
microfrontends.config.toClientConfig({
|
|
2068
|
-
removeFlaggedPaths: true
|
|
2069
|
-
}).serialize()
|
|
2070
|
-
),
|
|
2071
|
-
...app.getAssetPrefix() ? {
|
|
2072
|
-
NEXT_PUBLIC_VERCEL_FIREWALL_PATH_PREFIX: `/${app.getAssetPrefix()}`
|
|
2073
|
-
} : {},
|
|
2074
|
-
...process.env.ROUTE_OBSERVABILITY_TO_THIS_PROJECT && app.getAssetPrefix() ? {
|
|
2075
|
-
NEXT_PUBLIC_VERCEL_OBSERVABILITY_BASEPATH: `/${app.getAssetPrefix()}/_vercel`
|
|
2076
|
-
} : {}
|
|
2077
|
-
};
|
|
2078
|
-
const serverEnvs = {
|
|
2079
|
-
MFE_CURRENT_APPLICATION: app.name,
|
|
2080
|
-
MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
|
|
2081
|
-
};
|
|
2082
|
-
const allEnvs = { ...clientEnvs, ...serverEnvs };
|
|
2083
|
-
for (const [key, value] of Object.entries(allEnvs)) {
|
|
2084
|
-
process.env[key] = value;
|
|
2085
|
-
}
|
|
2086
|
-
debugEnv(allEnvs);
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
2165
|
// src/next/config/index.ts
|
|
2090
2166
|
function typedEntries(obj) {
|
|
2091
2167
|
return Object.entries(obj);
|