@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
|
@@ -1,39 +1,28 @@
|
|
|
1
1
|
// src/config/microfrontends/server/index.ts
|
|
2
2
|
import fs6 from "node:fs";
|
|
3
|
-
import { dirname as dirname2, join as
|
|
3
|
+
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
4
4
|
|
|
5
|
-
// src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function isOverrideCookie(cookie) {
|
|
11
|
-
return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
|
|
5
|
+
// src/bin/logger.ts
|
|
6
|
+
function debug(...args) {
|
|
7
|
+
if (process.env.MFE_DEBUG) {
|
|
8
|
+
console.log(...args);
|
|
9
|
+
}
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function getOverrideFromCookie(cookie) {
|
|
16
|
-
if (!isOverrideCookie(cookie) || !cookie.value)
|
|
17
|
-
return;
|
|
18
|
-
return {
|
|
19
|
-
application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
|
|
20
|
-
host: cookie.value
|
|
21
|
-
};
|
|
11
|
+
function info(...args) {
|
|
12
|
+
console.log(...args);
|
|
22
13
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const override = getOverrideFromCookie(cookie);
|
|
29
|
-
if (!override)
|
|
30
|
-
return;
|
|
31
|
-
overridesConfig.applications[override.application] = {
|
|
32
|
-
environment: { host: override.host }
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
return overridesConfig;
|
|
14
|
+
function warn(...args) {
|
|
15
|
+
console.warn(...args);
|
|
16
|
+
}
|
|
17
|
+
function error(...args) {
|
|
18
|
+
console.error(...args);
|
|
36
19
|
}
|
|
20
|
+
var logger = {
|
|
21
|
+
debug,
|
|
22
|
+
info,
|
|
23
|
+
warn,
|
|
24
|
+
error
|
|
25
|
+
};
|
|
37
26
|
|
|
38
27
|
// src/config/errors.ts
|
|
39
28
|
var MicrofrontendError = class extends Error {
|
|
@@ -127,277 +116,47 @@ var MicrofrontendError = class extends Error {
|
|
|
127
116
|
}
|
|
128
117
|
};
|
|
129
118
|
|
|
130
|
-
// src/config/microfrontends-config/
|
|
131
|
-
function getConfigStringFromEnv() {
|
|
132
|
-
const config = process.env.MFE_CONFIG;
|
|
133
|
-
if (!config) {
|
|
134
|
-
throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
|
|
135
|
-
type: "config",
|
|
136
|
-
subtype: "not_found_in_env"
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
return config;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/config/schema/utils/is-default-app.ts
|
|
143
|
-
function isDefaultApp(a) {
|
|
144
|
-
return !("routing" in a);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// src/config/microfrontends/utils/find-repository-root.ts
|
|
148
|
-
import fs from "node:fs";
|
|
149
|
-
import path from "node:path";
|
|
150
|
-
var GIT_DIRECTORY = ".git";
|
|
151
|
-
function hasGitDirectory(dir) {
|
|
152
|
-
const gitPath = path.join(dir, GIT_DIRECTORY);
|
|
153
|
-
return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
|
|
154
|
-
}
|
|
155
|
-
function hasPnpmWorkspaces(dir) {
|
|
156
|
-
return fs.existsSync(path.join(dir, "pnpm-workspace.yaml"));
|
|
157
|
-
}
|
|
158
|
-
function hasPackageJson(dir) {
|
|
159
|
-
return fs.existsSync(path.join(dir, "package.json"));
|
|
160
|
-
}
|
|
161
|
-
function findRepositoryRoot(startDir) {
|
|
162
|
-
if (process.env.NX_WORKSPACE_ROOT) {
|
|
163
|
-
return process.env.NX_WORKSPACE_ROOT;
|
|
164
|
-
}
|
|
165
|
-
let currentDir = startDir || process.cwd();
|
|
166
|
-
let lastPackageJsonDir = null;
|
|
167
|
-
while (currentDir !== path.parse(currentDir).root) {
|
|
168
|
-
if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
|
|
169
|
-
return currentDir;
|
|
170
|
-
}
|
|
171
|
-
if (hasPackageJson(currentDir)) {
|
|
172
|
-
lastPackageJsonDir = currentDir;
|
|
173
|
-
}
|
|
174
|
-
currentDir = path.dirname(currentDir);
|
|
175
|
-
}
|
|
176
|
-
if (lastPackageJsonDir) {
|
|
177
|
-
return lastPackageJsonDir;
|
|
178
|
-
}
|
|
179
|
-
throw new Error(
|
|
180
|
-
`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.`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// src/config/microfrontends/utils/infer-microfrontends-location.ts
|
|
185
|
-
import { dirname } from "node:path";
|
|
186
|
-
import { readFileSync } from "node:fs";
|
|
119
|
+
// src/config/microfrontends-config/isomorphic/index.ts
|
|
187
120
|
import { parse } from "jsonc-parser";
|
|
188
|
-
import fg from "fast-glob";
|
|
189
|
-
|
|
190
|
-
// src/config/microfrontends/utils/get-config-file-name.ts
|
|
191
|
-
var DEFAULT_CONFIGURATION_FILENAMES = [
|
|
192
|
-
"microfrontends.json",
|
|
193
|
-
"microfrontends.jsonc"
|
|
194
|
-
];
|
|
195
|
-
function getPossibleConfigurationFilenames({
|
|
196
|
-
customConfigFilename
|
|
197
|
-
}) {
|
|
198
|
-
if (customConfigFilename) {
|
|
199
|
-
if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
`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.`
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
return Array.from(
|
|
205
|
-
/* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
return DEFAULT_CONFIGURATION_FILENAMES;
|
|
209
|
-
}
|
|
210
121
|
|
|
211
|
-
// src/config/
|
|
212
|
-
var
|
|
213
|
-
|
|
214
|
-
repositoryRoot,
|
|
215
|
-
applicationContext,
|
|
216
|
-
customConfigFilename
|
|
217
|
-
}) {
|
|
218
|
-
const applicationName = applicationContext.name;
|
|
219
|
-
try {
|
|
220
|
-
const microfrontendsJsonPaths = fg.globSync(
|
|
221
|
-
`**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
|
|
222
|
-
{
|
|
223
|
-
cwd: repositoryRoot,
|
|
224
|
-
absolute: true,
|
|
225
|
-
onlyFiles: true,
|
|
226
|
-
followSymbolicLinks: false,
|
|
227
|
-
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
228
|
-
}
|
|
229
|
-
);
|
|
230
|
-
const matchingPaths = [];
|
|
231
|
-
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
232
|
-
try {
|
|
233
|
-
const microfrontendsJsonContent = readFileSync(
|
|
234
|
-
microfrontendsJsonPath,
|
|
235
|
-
"utf-8"
|
|
236
|
-
);
|
|
237
|
-
const microfrontendsJson = parse(microfrontendsJsonContent);
|
|
238
|
-
if (microfrontendsJson.applications[applicationName]) {
|
|
239
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
240
|
-
} else {
|
|
241
|
-
for (const [_, app] of Object.entries(
|
|
242
|
-
microfrontendsJson.applications
|
|
243
|
-
)) {
|
|
244
|
-
if (app.packageName === applicationName) {
|
|
245
|
-
matchingPaths.push(microfrontendsJsonPath);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
} catch (error2) {
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (matchingPaths.length > 1) {
|
|
253
|
-
throw new MicrofrontendError(
|
|
254
|
-
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
|
|
255
|
-
${matchingPaths.join("\n \u2022 ")}`,
|
|
256
|
-
{ type: "config", subtype: "inference_failed" }
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
if (matchingPaths.length === 0) {
|
|
260
|
-
let additionalErrorMessage = "";
|
|
261
|
-
if (microfrontendsJsonPaths.length > 0) {
|
|
262
|
-
if (!applicationContext.projectName) {
|
|
263
|
-
additionalErrorMessage = `
|
|
264
|
-
|
|
265
|
-
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.`;
|
|
266
|
-
} else {
|
|
267
|
-
additionalErrorMessage = `
|
|
268
|
-
|
|
269
|
-
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
throw new MicrofrontendError(
|
|
273
|
-
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
274
|
-
|
|
275
|
-
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.
|
|
276
|
-
|
|
277
|
-
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.
|
|
278
|
-
|
|
279
|
-
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
280
|
-
{ type: "config", subtype: "inference_failed" }
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
const [packageJsonPath] = matchingPaths;
|
|
284
|
-
return dirname(packageJsonPath);
|
|
285
|
-
} catch (error2) {
|
|
286
|
-
if (error2 instanceof MicrofrontendError) {
|
|
287
|
-
throw error2;
|
|
288
|
-
}
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
function inferMicrofrontendsLocation(opts) {
|
|
293
|
-
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
|
|
294
|
-
if (configCache[cacheKey]) {
|
|
295
|
-
return configCache[cacheKey];
|
|
296
|
-
}
|
|
297
|
-
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
298
|
-
if (!result) {
|
|
299
|
-
throw new MicrofrontendError(
|
|
300
|
-
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
301
|
-
{ type: "config", subtype: "inference_failed" }
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
configCache[cacheKey] = result;
|
|
305
|
-
return result;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// src/config/microfrontends/utils/is-monorepo.ts
|
|
309
|
-
import fs2 from "node:fs";
|
|
310
|
-
import path2 from "node:path";
|
|
122
|
+
// src/config/overrides/constants.ts
|
|
123
|
+
var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
|
|
124
|
+
var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
|
|
311
125
|
|
|
312
|
-
// src/
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
console.log(...args);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
function info(...args) {
|
|
319
|
-
console.log(...args);
|
|
320
|
-
}
|
|
321
|
-
function warn(...args) {
|
|
322
|
-
console.warn(...args);
|
|
323
|
-
}
|
|
324
|
-
function error(...args) {
|
|
325
|
-
console.error(...args);
|
|
126
|
+
// src/config/overrides/is-override-cookie.ts
|
|
127
|
+
function isOverrideCookie(cookie) {
|
|
128
|
+
return Boolean(cookie.name?.startsWith(OVERRIDES_COOKIE_PREFIX));
|
|
326
129
|
}
|
|
327
|
-
var logger = {
|
|
328
|
-
debug,
|
|
329
|
-
info,
|
|
330
|
-
warn,
|
|
331
|
-
error
|
|
332
|
-
};
|
|
333
130
|
|
|
334
|
-
// src/config/
|
|
335
|
-
function
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
if (process.env.NX_WORKSPACE_ROOT === path2.resolve(repositoryRoot)) {
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
348
|
-
const packageJsonPath = path2.join(repositoryRoot, "package.json");
|
|
349
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
const packageJson = JSON.parse(
|
|
353
|
-
fs2.readFileSync(packageJsonPath, "utf-8")
|
|
354
|
-
);
|
|
355
|
-
return packageJson.workspaces !== void 0;
|
|
356
|
-
} catch (error2) {
|
|
357
|
-
logger.error("Error determining if repository is a monorepo", error2);
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
131
|
+
// src/config/overrides/get-override-from-cookie.ts
|
|
132
|
+
function getOverrideFromCookie(cookie) {
|
|
133
|
+
if (!isOverrideCookie(cookie) || !cookie.value)
|
|
134
|
+
return;
|
|
135
|
+
return {
|
|
136
|
+
application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
|
|
137
|
+
host: cookie.value
|
|
138
|
+
};
|
|
360
139
|
}
|
|
361
140
|
|
|
362
|
-
// src/config/
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
throw new Error(
|
|
376
|
-
`The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
|
|
377
|
-
);
|
|
141
|
+
// src/config/overrides/parse-overrides.ts
|
|
142
|
+
function parseOverrides(cookies) {
|
|
143
|
+
const overridesConfig = { applications: {} };
|
|
144
|
+
cookies.forEach((cookie) => {
|
|
145
|
+
const override = getOverrideFromCookie(cookie);
|
|
146
|
+
if (!override)
|
|
147
|
+
return;
|
|
148
|
+
overridesConfig.applications[override.application] = {
|
|
149
|
+
environment: { host: override.host }
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
return overridesConfig;
|
|
378
153
|
}
|
|
379
154
|
|
|
380
|
-
// src/config/
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
function findConfig({
|
|
384
|
-
dir,
|
|
385
|
-
customConfigFilename
|
|
386
|
-
}) {
|
|
387
|
-
for (const filename of getPossibleConfigurationFilenames({
|
|
388
|
-
customConfigFilename
|
|
389
|
-
})) {
|
|
390
|
-
const maybeConfig = join(dir, filename);
|
|
391
|
-
if (fs4.existsSync(maybeConfig)) {
|
|
392
|
-
return maybeConfig;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return null;
|
|
155
|
+
// src/config/schema/utils/is-default-app.ts
|
|
156
|
+
function isDefaultApp(a) {
|
|
157
|
+
return !("routing" in a);
|
|
396
158
|
}
|
|
397
159
|
|
|
398
|
-
// src/config/microfrontends-config/isomorphic/index.ts
|
|
399
|
-
import { parse as parse2 } from "jsonc-parser";
|
|
400
|
-
|
|
401
160
|
// src/config/microfrontends-config/client/index.ts
|
|
402
161
|
import { pathToRegexp } from "path-to-regexp";
|
|
403
162
|
var regexpCache = /* @__PURE__ */ new Map();
|
|
@@ -456,48 +215,225 @@ var MicrofrontendConfigClient = class {
|
|
|
456
215
|
"Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy"
|
|
457
216
|
);
|
|
458
217
|
}
|
|
459
|
-
return new MicrofrontendConfigClient(JSON.parse(config));
|
|
218
|
+
return new MicrofrontendConfigClient(JSON.parse(config));
|
|
219
|
+
}
|
|
220
|
+
isEqual(other) {
|
|
221
|
+
return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
222
|
+
}
|
|
223
|
+
getApplicationNameForPath(path6) {
|
|
224
|
+
if (!path6.startsWith("/")) {
|
|
225
|
+
throw new Error(`Path must start with a /`);
|
|
226
|
+
}
|
|
227
|
+
if (this.pathCache[path6]) {
|
|
228
|
+
return this.pathCache[path6];
|
|
229
|
+
}
|
|
230
|
+
const pathname = new URL(path6, "https://example.com").pathname;
|
|
231
|
+
for (const [name, application] of Object.entries(this.applications)) {
|
|
232
|
+
if (application.routing) {
|
|
233
|
+
for (const group of application.routing) {
|
|
234
|
+
for (const childPath of group.paths) {
|
|
235
|
+
const regexp = getRegexp(childPath);
|
|
236
|
+
if (regexp.test(pathname)) {
|
|
237
|
+
this.pathCache[path6] = name;
|
|
238
|
+
return name;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const defaultApplication = Object.entries(this.applications).find(
|
|
245
|
+
([, application]) => application.default
|
|
246
|
+
);
|
|
247
|
+
if (!defaultApplication) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
this.pathCache[path6] = defaultApplication[0];
|
|
251
|
+
return defaultApplication[0];
|
|
252
|
+
}
|
|
253
|
+
serialize() {
|
|
254
|
+
return this.serialized;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/config/microfrontends-config/utils/get-config-from-env.ts
|
|
259
|
+
function getConfigStringFromEnv() {
|
|
260
|
+
const config = process.env.MFE_CONFIG;
|
|
261
|
+
if (!config) {
|
|
262
|
+
throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
|
|
263
|
+
type: "config",
|
|
264
|
+
subtype: "not_found_in_env"
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return config;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/config/microfrontends-config/isomorphic/constants.ts
|
|
271
|
+
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
272
|
+
var MFE_APP_PORT_ENV = "MFE_APP_PORT";
|
|
273
|
+
var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
|
|
274
|
+
|
|
275
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
276
|
+
function generatePortFromName({
|
|
277
|
+
name,
|
|
278
|
+
minPort = 3e3,
|
|
279
|
+
maxPort = 8e3
|
|
280
|
+
}) {
|
|
281
|
+
if (!name) {
|
|
282
|
+
throw new Error("Name is required to generate a port");
|
|
283
|
+
}
|
|
284
|
+
let hash = 0;
|
|
285
|
+
for (let i = 0; i < name.length; i++) {
|
|
286
|
+
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
287
|
+
hash |= 0;
|
|
288
|
+
}
|
|
289
|
+
hash = Math.abs(hash);
|
|
290
|
+
const range = maxPort - minPort;
|
|
291
|
+
const port = minPort + hash % range;
|
|
292
|
+
return port;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/config/microfrontends-config/isomorphic/host.ts
|
|
296
|
+
var Host = class {
|
|
297
|
+
constructor(hostConfig, options) {
|
|
298
|
+
if (typeof hostConfig === "string") {
|
|
299
|
+
({
|
|
300
|
+
protocol: this.protocol,
|
|
301
|
+
host: this.host,
|
|
302
|
+
port: this.port
|
|
303
|
+
} = Host.parseUrl(hostConfig));
|
|
304
|
+
} else {
|
|
305
|
+
const { protocol = "https", host, port } = hostConfig;
|
|
306
|
+
this.protocol = protocol;
|
|
307
|
+
this.host = host;
|
|
308
|
+
this.port = port;
|
|
309
|
+
}
|
|
310
|
+
this.local = options?.isLocal;
|
|
311
|
+
}
|
|
312
|
+
static parseUrl(url, defaultProtocol = "https") {
|
|
313
|
+
let hostToParse = url;
|
|
314
|
+
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
315
|
+
hostToParse = `${defaultProtocol}://${hostToParse}`;
|
|
316
|
+
}
|
|
317
|
+
const parsed = new URL(hostToParse);
|
|
318
|
+
if (!parsed.hostname) {
|
|
319
|
+
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
320
|
+
}
|
|
321
|
+
if (parsed.hash) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (parsed.username || parsed.password) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
Host.getMicrofrontendsError(
|
|
329
|
+
url,
|
|
330
|
+
"cannot have authentication credentials (username and/or password)"
|
|
331
|
+
)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
if (parsed.pathname !== "/") {
|
|
335
|
+
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
336
|
+
}
|
|
337
|
+
if (parsed.search) {
|
|
338
|
+
throw new Error(
|
|
339
|
+
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
const protocol = parsed.protocol.slice(0, -1);
|
|
343
|
+
return {
|
|
344
|
+
protocol,
|
|
345
|
+
host: parsed.hostname,
|
|
346
|
+
port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
|
|
347
|
+
};
|
|
460
348
|
}
|
|
461
|
-
|
|
462
|
-
return
|
|
349
|
+
static getMicrofrontendsError(url, message) {
|
|
350
|
+
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
463
351
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
352
|
+
isLocal() {
|
|
353
|
+
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
354
|
+
}
|
|
355
|
+
toString() {
|
|
356
|
+
const url = this.toUrl();
|
|
357
|
+
return url.toString().replace(/\/$/, "");
|
|
358
|
+
}
|
|
359
|
+
toUrl() {
|
|
360
|
+
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
361
|
+
return new URL(url);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var LocalHost = class extends Host {
|
|
365
|
+
constructor({
|
|
366
|
+
appName,
|
|
367
|
+
local
|
|
368
|
+
}) {
|
|
369
|
+
const portOverride = process.env[MFE_APP_PORT_ENV];
|
|
370
|
+
if (portOverride) {
|
|
371
|
+
const overridePort = Number.parseInt(portOverride, 10);
|
|
372
|
+
if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
|
|
373
|
+
super({
|
|
374
|
+
protocol: "http",
|
|
375
|
+
host: "localhost",
|
|
376
|
+
port: overridePort
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
483
379
|
}
|
|
484
380
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (
|
|
489
|
-
|
|
381
|
+
let protocol;
|
|
382
|
+
let host;
|
|
383
|
+
let port;
|
|
384
|
+
if (typeof local === "number") {
|
|
385
|
+
port = local;
|
|
386
|
+
} else if (typeof local === "string") {
|
|
387
|
+
if (/^\d+$/.test(local)) {
|
|
388
|
+
port = Number.parseInt(local, 10);
|
|
389
|
+
} else {
|
|
390
|
+
const parsed = Host.parseUrl(local, "http");
|
|
391
|
+
protocol = parsed.protocol;
|
|
392
|
+
host = parsed.host;
|
|
393
|
+
port = parsed.port;
|
|
394
|
+
}
|
|
395
|
+
} else if (local) {
|
|
396
|
+
protocol = local.protocol;
|
|
397
|
+
host = local.host;
|
|
398
|
+
port = local.port;
|
|
490
399
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
400
|
+
super({
|
|
401
|
+
protocol: protocol ?? "http",
|
|
402
|
+
host: host ?? "localhost",
|
|
403
|
+
port: port ?? generatePortFromName({ name: appName })
|
|
404
|
+
});
|
|
496
405
|
}
|
|
497
406
|
};
|
|
498
407
|
|
|
408
|
+
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
409
|
+
import md5 from "md5";
|
|
410
|
+
function hashApplicationName(name) {
|
|
411
|
+
if (!name) {
|
|
412
|
+
throw new Error("Application name is required to generate hash");
|
|
413
|
+
}
|
|
414
|
+
return md5(name).substring(0, 6).padStart(6, "0");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
418
|
+
var PREFIX = "vc-ap";
|
|
419
|
+
function generateAssetPrefixFromName({
|
|
420
|
+
name
|
|
421
|
+
}) {
|
|
422
|
+
if (!name) {
|
|
423
|
+
throw new Error("Name is required to generate an asset prefix");
|
|
424
|
+
}
|
|
425
|
+
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
|
|
429
|
+
function generateAutomationBypassEnvVarName({
|
|
430
|
+
name
|
|
431
|
+
}) {
|
|
432
|
+
return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
433
|
+
}
|
|
434
|
+
|
|
499
435
|
// src/config/microfrontends-config/isomorphic/validation.ts
|
|
500
|
-
import {
|
|
436
|
+
import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
|
|
501
437
|
var LIST_FORMATTER = new Intl.ListFormat("en", {
|
|
502
438
|
style: "long",
|
|
503
439
|
type: "conjunction"
|
|
@@ -673,157 +609,9 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
673
609
|
throw new MicrofrontendError(
|
|
674
610
|
`All applications except for the default app must contain the "routing" field. Applications that are missing routing: ${LIST_FORMATTER.format(applicationNamesMissingRouting)}.`,
|
|
675
611
|
{ type: "config", subtype: "multiple_default_applications" }
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
// src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
|
|
681
|
-
import md5 from "md5";
|
|
682
|
-
function hashApplicationName(name) {
|
|
683
|
-
if (!name) {
|
|
684
|
-
throw new Error("Application name is required to generate hash");
|
|
685
|
-
}
|
|
686
|
-
return md5(name).substring(0, 6).padStart(6, "0");
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
690
|
-
var PREFIX = "vc-ap";
|
|
691
|
-
function generateAssetPrefixFromName({
|
|
692
|
-
name
|
|
693
|
-
}) {
|
|
694
|
-
if (!name) {
|
|
695
|
-
throw new Error("Name is required to generate an asset prefix");
|
|
696
|
-
}
|
|
697
|
-
return `${PREFIX}-${hashApplicationName(name)}`;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
701
|
-
function generatePortFromName({
|
|
702
|
-
name,
|
|
703
|
-
minPort = 3e3,
|
|
704
|
-
maxPort = 8e3
|
|
705
|
-
}) {
|
|
706
|
-
if (!name) {
|
|
707
|
-
throw new Error("Name is required to generate a port");
|
|
708
|
-
}
|
|
709
|
-
let hash = 0;
|
|
710
|
-
for (let i = 0; i < name.length; i++) {
|
|
711
|
-
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
712
|
-
hash |= 0;
|
|
713
|
-
}
|
|
714
|
-
hash = Math.abs(hash);
|
|
715
|
-
const range = maxPort - minPort;
|
|
716
|
-
const port = minPort + hash % range;
|
|
717
|
-
return port;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
// src/config/microfrontends-config/isomorphic/host.ts
|
|
721
|
-
var Host = class {
|
|
722
|
-
constructor(hostConfig, options) {
|
|
723
|
-
if (typeof hostConfig === "string") {
|
|
724
|
-
({
|
|
725
|
-
protocol: this.protocol,
|
|
726
|
-
host: this.host,
|
|
727
|
-
port: this.port
|
|
728
|
-
} = Host.parseUrl(hostConfig));
|
|
729
|
-
} else {
|
|
730
|
-
const { protocol = "https", host, port } = hostConfig;
|
|
731
|
-
this.protocol = protocol;
|
|
732
|
-
this.host = host;
|
|
733
|
-
this.port = port;
|
|
734
|
-
}
|
|
735
|
-
this.local = options?.isLocal;
|
|
736
|
-
}
|
|
737
|
-
static parseUrl(url, defaultProtocol = "https") {
|
|
738
|
-
let hostToParse = url;
|
|
739
|
-
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
740
|
-
hostToParse = `${defaultProtocol}://${hostToParse}`;
|
|
741
|
-
}
|
|
742
|
-
const parsed = new URL(hostToParse);
|
|
743
|
-
if (!parsed.hostname) {
|
|
744
|
-
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
745
|
-
}
|
|
746
|
-
if (parsed.hash) {
|
|
747
|
-
throw new Error(
|
|
748
|
-
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
749
|
-
);
|
|
750
|
-
}
|
|
751
|
-
if (parsed.username || parsed.password) {
|
|
752
|
-
throw new Error(
|
|
753
|
-
Host.getMicrofrontendsError(
|
|
754
|
-
url,
|
|
755
|
-
"cannot have authentication credentials (username and/or password)"
|
|
756
|
-
)
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
if (parsed.pathname !== "/") {
|
|
760
|
-
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
761
|
-
}
|
|
762
|
-
if (parsed.search) {
|
|
763
|
-
throw new Error(
|
|
764
|
-
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
const protocol = parsed.protocol.slice(0, -1);
|
|
768
|
-
return {
|
|
769
|
-
protocol,
|
|
770
|
-
host: parsed.hostname,
|
|
771
|
-
port: parsed.port ? Number.parseInt(parsed.port) : void 0
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
static getMicrofrontendsError(url, message) {
|
|
775
|
-
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
776
|
-
}
|
|
777
|
-
isLocal() {
|
|
778
|
-
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
779
|
-
}
|
|
780
|
-
toString() {
|
|
781
|
-
const url = this.toUrl();
|
|
782
|
-
return url.toString().replace(/\/$/, "");
|
|
783
|
-
}
|
|
784
|
-
toUrl() {
|
|
785
|
-
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
786
|
-
return new URL(url);
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
var LocalHost = class extends Host {
|
|
790
|
-
constructor({
|
|
791
|
-
appName,
|
|
792
|
-
local
|
|
793
|
-
}) {
|
|
794
|
-
let protocol;
|
|
795
|
-
let host;
|
|
796
|
-
let port;
|
|
797
|
-
if (typeof local === "number") {
|
|
798
|
-
port = local;
|
|
799
|
-
} else if (typeof local === "string") {
|
|
800
|
-
if (/^\d+$/.test(local)) {
|
|
801
|
-
port = Number.parseInt(local);
|
|
802
|
-
} else {
|
|
803
|
-
const parsed = Host.parseUrl(local, "http");
|
|
804
|
-
protocol = parsed.protocol;
|
|
805
|
-
host = parsed.host;
|
|
806
|
-
port = parsed.port;
|
|
807
|
-
}
|
|
808
|
-
} else if (local) {
|
|
809
|
-
protocol = local.protocol;
|
|
810
|
-
host = local.host;
|
|
811
|
-
port = local.port;
|
|
812
|
-
}
|
|
813
|
-
super({
|
|
814
|
-
protocol: protocol ?? "http",
|
|
815
|
-
host: host ?? "localhost",
|
|
816
|
-
port: port ?? generatePortFromName({ name: appName })
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
// src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
|
|
822
|
-
function generateAutomationBypassEnvVarName({
|
|
823
|
-
name
|
|
824
|
-
}) {
|
|
825
|
-
return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
826
|
-
}
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
827
615
|
|
|
828
616
|
// src/config/microfrontends-config/isomorphic/application.ts
|
|
829
617
|
var Application = class {
|
|
@@ -905,9 +693,6 @@ var ChildApplication = class extends Application {
|
|
|
905
693
|
}
|
|
906
694
|
};
|
|
907
695
|
|
|
908
|
-
// src/config/microfrontends-config/isomorphic/constants.ts
|
|
909
|
-
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
910
|
-
|
|
911
696
|
// src/config/microfrontends-config/isomorphic/index.ts
|
|
912
697
|
var MicrofrontendConfigIsomorphic = class {
|
|
913
698
|
constructor({
|
|
@@ -951,7 +736,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
951
736
|
};
|
|
952
737
|
}
|
|
953
738
|
static validate(config) {
|
|
954
|
-
const c = typeof config === "string" ?
|
|
739
|
+
const c = typeof config === "string" ? parse(config) : config;
|
|
955
740
|
validateConfigPaths(c.applications);
|
|
956
741
|
validateConfigDefaultApplication(c.applications);
|
|
957
742
|
return c;
|
|
@@ -960,7 +745,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
960
745
|
cookies
|
|
961
746
|
}) {
|
|
962
747
|
return new MicrofrontendConfigIsomorphic({
|
|
963
|
-
config:
|
|
748
|
+
config: parse(getConfigStringFromEnv()),
|
|
964
749
|
overrides: parseOverrides(cookies ?? [])
|
|
965
750
|
});
|
|
966
751
|
}
|
|
@@ -1026,9 +811,17 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1026
811
|
return this.defaultApplication;
|
|
1027
812
|
}
|
|
1028
813
|
/**
|
|
1029
|
-
* Returns the configured port for the local proxy
|
|
814
|
+
* Returns the configured port for the local proxy.
|
|
815
|
+
* Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
|
|
1030
816
|
*/
|
|
1031
817
|
getLocalProxyPort() {
|
|
818
|
+
const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
|
|
819
|
+
if (portOverride) {
|
|
820
|
+
const port = Number.parseInt(portOverride, 10);
|
|
821
|
+
if (!Number.isNaN(port) && port > 0 && port < 65536) {
|
|
822
|
+
return port;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
1032
825
|
return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
|
|
1033
826
|
}
|
|
1034
827
|
toClientConfig(options) {
|
|
@@ -1066,32 +859,144 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
1066
859
|
}
|
|
1067
860
|
};
|
|
1068
861
|
|
|
862
|
+
// src/config/microfrontends/utils/find-config.ts
|
|
863
|
+
import fs from "node:fs";
|
|
864
|
+
import { join } from "node:path";
|
|
865
|
+
|
|
866
|
+
// src/config/microfrontends/utils/get-config-file-name.ts
|
|
867
|
+
var DEFAULT_CONFIGURATION_FILENAMES = [
|
|
868
|
+
"microfrontends.json",
|
|
869
|
+
"microfrontends.jsonc"
|
|
870
|
+
];
|
|
871
|
+
function getPossibleConfigurationFilenames({
|
|
872
|
+
customConfigFilename
|
|
873
|
+
}) {
|
|
874
|
+
if (customConfigFilename) {
|
|
875
|
+
if (!customConfigFilename.endsWith(".json") && !customConfigFilename.endsWith(".jsonc")) {
|
|
876
|
+
throw new Error(
|
|
877
|
+
`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.`
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
return Array.from(
|
|
881
|
+
/* @__PURE__ */ new Set([customConfigFilename, ...DEFAULT_CONFIGURATION_FILENAMES])
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
return DEFAULT_CONFIGURATION_FILENAMES;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/config/microfrontends/utils/find-config.ts
|
|
888
|
+
function findConfig({
|
|
889
|
+
dir,
|
|
890
|
+
customConfigFilename
|
|
891
|
+
}) {
|
|
892
|
+
for (const filename of getPossibleConfigurationFilenames({
|
|
893
|
+
customConfigFilename
|
|
894
|
+
})) {
|
|
895
|
+
const maybeConfig = join(dir, filename);
|
|
896
|
+
if (fs.existsSync(maybeConfig)) {
|
|
897
|
+
return maybeConfig;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/config/microfrontends/utils/find-package-root.ts
|
|
904
|
+
import fs2 from "node:fs";
|
|
905
|
+
import path from "node:path";
|
|
906
|
+
var PACKAGE_JSON = "package.json";
|
|
907
|
+
function findPackageRoot(startDir) {
|
|
908
|
+
let currentDir = startDir || process.cwd();
|
|
909
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
910
|
+
const pkgJsonPath = path.join(currentDir, PACKAGE_JSON);
|
|
911
|
+
if (fs2.existsSync(pkgJsonPath)) {
|
|
912
|
+
return currentDir;
|
|
913
|
+
}
|
|
914
|
+
currentDir = path.dirname(currentDir);
|
|
915
|
+
}
|
|
916
|
+
throw new Error(
|
|
917
|
+
`The root of the package that contains the \`package.json\` file for the \`${startDir}\` directory could not be found.`
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// src/config/microfrontends/utils/find-repository-root.ts
|
|
922
|
+
import fs3 from "node:fs";
|
|
923
|
+
import path2 from "node:path";
|
|
924
|
+
var GIT_DIRECTORY = ".git";
|
|
925
|
+
function hasGitDirectory(dir) {
|
|
926
|
+
const gitPath = path2.join(dir, GIT_DIRECTORY);
|
|
927
|
+
return fs3.existsSync(gitPath) && fs3.statSync(gitPath).isDirectory();
|
|
928
|
+
}
|
|
929
|
+
function hasPnpmWorkspaces(dir) {
|
|
930
|
+
return fs3.existsSync(path2.join(dir, "pnpm-workspace.yaml"));
|
|
931
|
+
}
|
|
932
|
+
function hasPackageJson(dir) {
|
|
933
|
+
return fs3.existsSync(path2.join(dir, "package.json"));
|
|
934
|
+
}
|
|
935
|
+
function findRepositoryRoot(startDir) {
|
|
936
|
+
if (process.env.NX_WORKSPACE_ROOT) {
|
|
937
|
+
return process.env.NX_WORKSPACE_ROOT;
|
|
938
|
+
}
|
|
939
|
+
let currentDir = startDir || process.cwd();
|
|
940
|
+
let lastPackageJsonDir = null;
|
|
941
|
+
while (currentDir !== path2.parse(currentDir).root) {
|
|
942
|
+
if (hasGitDirectory(currentDir) || hasPnpmWorkspaces(currentDir)) {
|
|
943
|
+
return currentDir;
|
|
944
|
+
}
|
|
945
|
+
if (hasPackageJson(currentDir)) {
|
|
946
|
+
lastPackageJsonDir = currentDir;
|
|
947
|
+
}
|
|
948
|
+
currentDir = path2.dirname(currentDir);
|
|
949
|
+
}
|
|
950
|
+
if (lastPackageJsonDir) {
|
|
951
|
+
return lastPackageJsonDir;
|
|
952
|
+
}
|
|
953
|
+
throw new Error(
|
|
954
|
+
`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.`
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
1069
958
|
// src/config/microfrontends/utils/get-application-context.ts
|
|
1070
|
-
import
|
|
1071
|
-
import
|
|
959
|
+
import fs4 from "node:fs";
|
|
960
|
+
import path3 from "node:path";
|
|
1072
961
|
function getApplicationContext(opts) {
|
|
1073
962
|
if (opts?.appName) {
|
|
963
|
+
logger.debug(
|
|
964
|
+
"[MFE Config] Application name from appName parameter:",
|
|
965
|
+
opts.appName
|
|
966
|
+
);
|
|
1074
967
|
return { name: opts.appName };
|
|
1075
968
|
}
|
|
1076
969
|
if (process.env.VERCEL_PROJECT_NAME) {
|
|
970
|
+
logger.debug(
|
|
971
|
+
"[MFE Config] Application name from VERCEL_PROJECT_NAME:",
|
|
972
|
+
process.env.VERCEL_PROJECT_NAME
|
|
973
|
+
);
|
|
1077
974
|
return {
|
|
1078
975
|
name: process.env.VERCEL_PROJECT_NAME,
|
|
1079
976
|
projectName: process.env.VERCEL_PROJECT_NAME
|
|
1080
977
|
};
|
|
1081
978
|
}
|
|
1082
979
|
if (process.env.NX_TASK_TARGET_PROJECT) {
|
|
980
|
+
logger.debug(
|
|
981
|
+
"[MFE Config] Application name from NX_TASK_TARGET_PROJECT:",
|
|
982
|
+
process.env.NX_TASK_TARGET_PROJECT
|
|
983
|
+
);
|
|
1083
984
|
return {
|
|
1084
985
|
name: process.env.NX_TASK_TARGET_PROJECT,
|
|
1085
986
|
packageJsonName: process.env.NX_TASK_TARGET_PROJECT
|
|
1086
987
|
};
|
|
1087
988
|
}
|
|
1088
989
|
try {
|
|
1089
|
-
const vercelProjectJsonPath =
|
|
1090
|
-
|
|
990
|
+
const vercelProjectJsonPath = fs4.readFileSync(
|
|
991
|
+
path3.join(opts?.packageRoot || ".", ".vercel", "project.json"),
|
|
1091
992
|
"utf-8"
|
|
1092
993
|
);
|
|
1093
994
|
const projectJson = JSON.parse(vercelProjectJsonPath);
|
|
1094
995
|
if (projectJson.projectName) {
|
|
996
|
+
logger.debug(
|
|
997
|
+
"[MFE Config] Application name from .vercel/project.json:",
|
|
998
|
+
projectJson.projectName
|
|
999
|
+
);
|
|
1095
1000
|
return {
|
|
1096
1001
|
name: projectJson.projectName,
|
|
1097
1002
|
projectName: projectJson.projectName
|
|
@@ -1100,8 +1005,8 @@ function getApplicationContext(opts) {
|
|
|
1100
1005
|
} catch (_) {
|
|
1101
1006
|
}
|
|
1102
1007
|
try {
|
|
1103
|
-
const packageJsonString =
|
|
1104
|
-
|
|
1008
|
+
const packageJsonString = fs4.readFileSync(
|
|
1009
|
+
path3.join(opts?.packageRoot || ".", "package.json"),
|
|
1105
1010
|
"utf-8"
|
|
1106
1011
|
);
|
|
1107
1012
|
const packageJson = JSON.parse(packageJsonString);
|
|
@@ -1115,6 +1020,10 @@ function getApplicationContext(opts) {
|
|
|
1115
1020
|
}
|
|
1116
1021
|
);
|
|
1117
1022
|
}
|
|
1023
|
+
logger.debug(
|
|
1024
|
+
"[MFE Config] Application name from package.json:",
|
|
1025
|
+
packageJson.name
|
|
1026
|
+
);
|
|
1118
1027
|
return { name: packageJson.name, packageJsonName: packageJson.name };
|
|
1119
1028
|
} catch (err) {
|
|
1120
1029
|
throw MicrofrontendError.handle(err, {
|
|
@@ -1123,6 +1032,209 @@ function getApplicationContext(opts) {
|
|
|
1123
1032
|
}
|
|
1124
1033
|
}
|
|
1125
1034
|
|
|
1035
|
+
// src/config/microfrontends/utils/infer-microfrontends-location.ts
|
|
1036
|
+
import { readFileSync, statSync } from "node:fs";
|
|
1037
|
+
import { dirname, join as join2 } from "node:path";
|
|
1038
|
+
import fg from "fast-glob";
|
|
1039
|
+
import { parse as parse2 } from "jsonc-parser";
|
|
1040
|
+
var configCache = {};
|
|
1041
|
+
function findPackageWithMicrofrontendsConfig({
|
|
1042
|
+
repositoryRoot,
|
|
1043
|
+
applicationContext,
|
|
1044
|
+
customConfigFilename
|
|
1045
|
+
}) {
|
|
1046
|
+
const applicationName = applicationContext.name;
|
|
1047
|
+
logger.debug(
|
|
1048
|
+
"[MFE Config] Searching repository for configs containing application:",
|
|
1049
|
+
applicationName
|
|
1050
|
+
);
|
|
1051
|
+
try {
|
|
1052
|
+
const microfrontendsJsonPaths = fg.globSync(
|
|
1053
|
+
`**/{${getPossibleConfigurationFilenames({ customConfigFilename }).join(",")}}`,
|
|
1054
|
+
{
|
|
1055
|
+
cwd: repositoryRoot,
|
|
1056
|
+
absolute: true,
|
|
1057
|
+
onlyFiles: true,
|
|
1058
|
+
followSymbolicLinks: false,
|
|
1059
|
+
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
1060
|
+
}
|
|
1061
|
+
);
|
|
1062
|
+
logger.debug(
|
|
1063
|
+
"[MFE Config] Found",
|
|
1064
|
+
microfrontendsJsonPaths.length,
|
|
1065
|
+
"config file(s) in repository"
|
|
1066
|
+
);
|
|
1067
|
+
const matchingPaths = [];
|
|
1068
|
+
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
|
|
1069
|
+
if (doesApplicationExistInConfig(microfrontendsJsonPath, applicationName)) {
|
|
1070
|
+
matchingPaths.push(microfrontendsJsonPath);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
logger.debug(
|
|
1074
|
+
"[MFE Config] Total matching config files:",
|
|
1075
|
+
matchingPaths.length
|
|
1076
|
+
);
|
|
1077
|
+
if (matchingPaths.length > 1) {
|
|
1078
|
+
throw new MicrofrontendError(
|
|
1079
|
+
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
|
|
1080
|
+
${matchingPaths.join("\n \u2022 ")}`,
|
|
1081
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
if (matchingPaths.length === 0) {
|
|
1085
|
+
if (repositoryRoot && doesMisplacedConfigExist(
|
|
1086
|
+
repositoryRoot,
|
|
1087
|
+
applicationName,
|
|
1088
|
+
customConfigFilename
|
|
1089
|
+
)) {
|
|
1090
|
+
logger.debug(
|
|
1091
|
+
"[MFE Config] Found misplaced config in wrong .vercel directory in repository"
|
|
1092
|
+
);
|
|
1093
|
+
const misplacedConfigPath = join2(
|
|
1094
|
+
repositoryRoot,
|
|
1095
|
+
".vercel",
|
|
1096
|
+
customConfigFilename || "microfrontends.json"
|
|
1097
|
+
);
|
|
1098
|
+
throw new MicrofrontendError(
|
|
1099
|
+
`Unable to automatically infer the location of the \`microfrontends.json\` file.
|
|
1100
|
+
|
|
1101
|
+
A microfrontends config was found in the \`.vercel\` directory at the repository root: ${misplacedConfigPath}
|
|
1102
|
+
However, in a monorepo, the config file should be placed in the \`.vercel\` directory in your application directory instead.
|
|
1103
|
+
|
|
1104
|
+
To fix this:
|
|
1105
|
+
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
|
|
1106
|
+
2. If manually defined, move the config file to the \`.vercel\` directory in your application
|
|
1107
|
+
3. Alternatively, set the VC_MICROFRONTENDS_CONFIG environment variable to the correct path
|
|
1108
|
+
|
|
1109
|
+
For more information, see: https://vercel.com/docs/cli/project-linking`,
|
|
1110
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
let additionalErrorMessage = "";
|
|
1114
|
+
if (microfrontendsJsonPaths.length > 0) {
|
|
1115
|
+
if (!applicationContext.projectName) {
|
|
1116
|
+
additionalErrorMessage = `
|
|
1117
|
+
|
|
1118
|
+
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.`;
|
|
1119
|
+
} else {
|
|
1120
|
+
additionalErrorMessage = `
|
|
1121
|
+
|
|
1122
|
+
Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
throw new MicrofrontendError(
|
|
1126
|
+
`Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
|
|
1127
|
+
|
|
1128
|
+
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.
|
|
1129
|
+
|
|
1130
|
+
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.
|
|
1131
|
+
|
|
1132
|
+
If you suspect this is thrown in error, please reach out to the Vercel team.`,
|
|
1133
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const [packageJsonPath] = matchingPaths;
|
|
1137
|
+
return dirname(packageJsonPath);
|
|
1138
|
+
} catch (error2) {
|
|
1139
|
+
if (error2 instanceof MicrofrontendError) {
|
|
1140
|
+
throw error2;
|
|
1141
|
+
}
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function inferMicrofrontendsLocation(opts) {
|
|
1146
|
+
const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}${opts.customConfigFilename ? `-${opts.customConfigFilename}` : ""}`;
|
|
1147
|
+
if (configCache[cacheKey]) {
|
|
1148
|
+
return configCache[cacheKey];
|
|
1149
|
+
}
|
|
1150
|
+
const result = findPackageWithMicrofrontendsConfig(opts);
|
|
1151
|
+
if (!result) {
|
|
1152
|
+
throw new MicrofrontendError(
|
|
1153
|
+
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
|
|
1154
|
+
{ type: "config", subtype: "inference_failed" }
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
configCache[cacheKey] = result;
|
|
1158
|
+
return result;
|
|
1159
|
+
}
|
|
1160
|
+
function existsSync(path6) {
|
|
1161
|
+
try {
|
|
1162
|
+
statSync(path6);
|
|
1163
|
+
return true;
|
|
1164
|
+
} catch (_) {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function doesMisplacedConfigExist(repositoryRoot, applicationName, customConfigFilename) {
|
|
1169
|
+
logger.debug(
|
|
1170
|
+
"[MFE Config] Looking for misplaced config in wrong .vercel directory"
|
|
1171
|
+
);
|
|
1172
|
+
const misplacedConfigPath = join2(
|
|
1173
|
+
repositoryRoot,
|
|
1174
|
+
".vercel",
|
|
1175
|
+
customConfigFilename || "microfrontends.json"
|
|
1176
|
+
);
|
|
1177
|
+
return existsSync(misplacedConfigPath) && doesApplicationExistInConfig(misplacedConfigPath, applicationName);
|
|
1178
|
+
}
|
|
1179
|
+
function doesApplicationExistInConfig(microfrontendsJsonPath, applicationName) {
|
|
1180
|
+
try {
|
|
1181
|
+
const microfrontendsJsonContent = readFileSync(
|
|
1182
|
+
microfrontendsJsonPath,
|
|
1183
|
+
"utf-8"
|
|
1184
|
+
);
|
|
1185
|
+
const microfrontendsJson = parse2(microfrontendsJsonContent);
|
|
1186
|
+
if (microfrontendsJson.applications[applicationName]) {
|
|
1187
|
+
logger.debug(
|
|
1188
|
+
"[MFE Config] Found application in config:",
|
|
1189
|
+
microfrontendsJsonPath
|
|
1190
|
+
);
|
|
1191
|
+
return true;
|
|
1192
|
+
}
|
|
1193
|
+
for (const [_, app] of Object.entries(microfrontendsJson.applications)) {
|
|
1194
|
+
if (app.packageName === applicationName) {
|
|
1195
|
+
logger.debug(
|
|
1196
|
+
"[MFE Config] Found application via packageName in config:",
|
|
1197
|
+
microfrontendsJsonPath
|
|
1198
|
+
);
|
|
1199
|
+
return true;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
} catch (error2) {
|
|
1203
|
+
logger.debug("[MFE Config] Error checking application in config:", error2);
|
|
1204
|
+
}
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/config/microfrontends/utils/is-monorepo.ts
|
|
1209
|
+
import fs5 from "node:fs";
|
|
1210
|
+
import path4 from "node:path";
|
|
1211
|
+
function isMonorepo({
|
|
1212
|
+
repositoryRoot
|
|
1213
|
+
}) {
|
|
1214
|
+
try {
|
|
1215
|
+
if (fs5.existsSync(path4.join(repositoryRoot, "pnpm-workspace.yaml"))) {
|
|
1216
|
+
return true;
|
|
1217
|
+
}
|
|
1218
|
+
if (fs5.existsSync(path4.join(repositoryRoot, "vlt-workspaces.json"))) {
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
if (process.env.NX_WORKSPACE_ROOT === path4.resolve(repositoryRoot)) {
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
const packageJsonPath = path4.join(repositoryRoot, "package.json");
|
|
1225
|
+
if (!fs5.existsSync(packageJsonPath)) {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
const packageJson = JSON.parse(
|
|
1229
|
+
fs5.readFileSync(packageJsonPath, "utf-8")
|
|
1230
|
+
);
|
|
1231
|
+
return packageJson.workspaces !== void 0;
|
|
1232
|
+
} catch (error2) {
|
|
1233
|
+
logger.error("Error determining if repository is a monorepo", error2);
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1126
1238
|
// src/config/microfrontends/server/utils/get-output-file-path.ts
|
|
1127
1239
|
import path5 from "node:path";
|
|
1128
1240
|
|
|
@@ -1136,8 +1248,8 @@ function getOutputFilePath() {
|
|
|
1136
1248
|
}
|
|
1137
1249
|
|
|
1138
1250
|
// src/config/microfrontends/server/validation.ts
|
|
1139
|
-
import { parse as parse3 } from "jsonc-parser";
|
|
1140
1251
|
import { Ajv } from "ajv";
|
|
1252
|
+
import { parse as parse3 } from "jsonc-parser";
|
|
1141
1253
|
|
|
1142
1254
|
// schema/schema.json
|
|
1143
1255
|
var schema_default = {
|
|
@@ -1165,9 +1277,7 @@ var schema_default = {
|
|
|
1165
1277
|
description: "Optional configuration options for the microfrontend."
|
|
1166
1278
|
}
|
|
1167
1279
|
},
|
|
1168
|
-
required: [
|
|
1169
|
-
"applications"
|
|
1170
|
-
],
|
|
1280
|
+
required: ["applications"],
|
|
1171
1281
|
additionalProperties: false,
|
|
1172
1282
|
description: "The microfrontends configuration schema. See https://vercel.com/docs/microfrontends/configuration."
|
|
1173
1283
|
},
|
|
@@ -1204,19 +1314,14 @@ var schema_default = {
|
|
|
1204
1314
|
description: "Development configuration for the default application."
|
|
1205
1315
|
}
|
|
1206
1316
|
},
|
|
1207
|
-
required: [
|
|
1208
|
-
"development"
|
|
1209
|
-
],
|
|
1317
|
+
required: ["development"],
|
|
1210
1318
|
additionalProperties: false
|
|
1211
1319
|
},
|
|
1212
1320
|
DefaultDevelopment: {
|
|
1213
1321
|
type: "object",
|
|
1214
1322
|
properties: {
|
|
1215
1323
|
local: {
|
|
1216
|
-
type: [
|
|
1217
|
-
"number",
|
|
1218
|
-
"string"
|
|
1219
|
-
],
|
|
1324
|
+
type: ["number", "string"],
|
|
1220
1325
|
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."
|
|
1221
1326
|
},
|
|
1222
1327
|
task: {
|
|
@@ -1228,9 +1333,7 @@ var schema_default = {
|
|
|
1228
1333
|
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."
|
|
1229
1334
|
}
|
|
1230
1335
|
},
|
|
1231
|
-
required: [
|
|
1232
|
-
"fallback"
|
|
1233
|
-
],
|
|
1336
|
+
required: ["fallback"],
|
|
1234
1337
|
additionalProperties: false
|
|
1235
1338
|
},
|
|
1236
1339
|
ChildApplication: {
|
|
@@ -1253,19 +1356,14 @@ var schema_default = {
|
|
|
1253
1356
|
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."
|
|
1254
1357
|
}
|
|
1255
1358
|
},
|
|
1256
|
-
required: [
|
|
1257
|
-
"routing"
|
|
1258
|
-
],
|
|
1359
|
+
required: ["routing"],
|
|
1259
1360
|
additionalProperties: false
|
|
1260
1361
|
},
|
|
1261
1362
|
ChildDevelopment: {
|
|
1262
1363
|
type: "object",
|
|
1263
1364
|
properties: {
|
|
1264
1365
|
local: {
|
|
1265
|
-
type: [
|
|
1266
|
-
"number",
|
|
1267
|
-
"string"
|
|
1268
|
-
],
|
|
1366
|
+
type: ["number", "string"],
|
|
1269
1367
|
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."
|
|
1270
1368
|
},
|
|
1271
1369
|
task: {
|
|
@@ -1305,9 +1403,7 @@ var schema_default = {
|
|
|
1305
1403
|
description: "A list of path expressions that are routed to this application. See https://vercel.com/docs/microfrontends/path-routing#supported-path-expressions."
|
|
1306
1404
|
}
|
|
1307
1405
|
},
|
|
1308
|
-
required: [
|
|
1309
|
-
"paths"
|
|
1310
|
-
],
|
|
1406
|
+
required: ["paths"],
|
|
1311
1407
|
additionalProperties: false,
|
|
1312
1408
|
description: "A group of paths that is routed to this application."
|
|
1313
1409
|
},
|
|
@@ -1486,7 +1582,13 @@ var MicrofrontendsServer = class {
|
|
|
1486
1582
|
filePath,
|
|
1487
1583
|
cookies
|
|
1488
1584
|
} = {}) {
|
|
1585
|
+
logger.debug("[MFE Config] Starting config inference", {
|
|
1586
|
+
appName,
|
|
1587
|
+
directory: directory || process.cwd(),
|
|
1588
|
+
filePath
|
|
1589
|
+
});
|
|
1489
1590
|
if (filePath) {
|
|
1591
|
+
logger.debug("[MFE Config] Using explicit filePath:", filePath);
|
|
1490
1592
|
return MicrofrontendsServer.fromFile({
|
|
1491
1593
|
filePath,
|
|
1492
1594
|
cookies
|
|
@@ -1494,16 +1596,25 @@ var MicrofrontendsServer = class {
|
|
|
1494
1596
|
}
|
|
1495
1597
|
try {
|
|
1496
1598
|
const packageRoot = findPackageRoot(directory);
|
|
1599
|
+
logger.debug("[MFE Config] Package root:", packageRoot);
|
|
1497
1600
|
const applicationContext = getApplicationContext({
|
|
1498
1601
|
appName,
|
|
1499
1602
|
packageRoot
|
|
1500
1603
|
});
|
|
1604
|
+
logger.debug("[MFE Config] Application context:", applicationContext);
|
|
1501
1605
|
const customConfigFilename = process.env.VC_MICROFRONTENDS_CONFIG_FILE_NAME;
|
|
1606
|
+
if (customConfigFilename) {
|
|
1607
|
+
logger.debug(
|
|
1608
|
+
"[MFE Config] Custom config filename from VC_MICROFRONTENDS_CONFIG_FILE_NAME:",
|
|
1609
|
+
customConfigFilename
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1502
1612
|
const maybeConfig = findConfig({
|
|
1503
1613
|
dir: packageRoot,
|
|
1504
1614
|
customConfigFilename
|
|
1505
1615
|
});
|
|
1506
1616
|
if (maybeConfig) {
|
|
1617
|
+
logger.debug("[MFE Config] Config found at package root:", maybeConfig);
|
|
1507
1618
|
return MicrofrontendsServer.fromFile({
|
|
1508
1619
|
filePath: maybeConfig,
|
|
1509
1620
|
cookies
|
|
@@ -1511,42 +1622,78 @@ var MicrofrontendsServer = class {
|
|
|
1511
1622
|
}
|
|
1512
1623
|
const repositoryRoot = findRepositoryRoot();
|
|
1513
1624
|
const isMonorepo2 = isMonorepo({ repositoryRoot });
|
|
1625
|
+
logger.debug(
|
|
1626
|
+
"[MFE Config] Repository root:",
|
|
1627
|
+
repositoryRoot,
|
|
1628
|
+
"Is monorepo:",
|
|
1629
|
+
isMonorepo2
|
|
1630
|
+
);
|
|
1514
1631
|
const configFromEnv = process.env.VC_MICROFRONTENDS_CONFIG;
|
|
1515
1632
|
if (typeof configFromEnv === "string") {
|
|
1633
|
+
logger.debug(
|
|
1634
|
+
"[MFE Config] Checking VC_MICROFRONTENDS_CONFIG:",
|
|
1635
|
+
configFromEnv
|
|
1636
|
+
);
|
|
1516
1637
|
const maybeConfigFromEnv = resolve(packageRoot, configFromEnv);
|
|
1517
1638
|
if (maybeConfigFromEnv) {
|
|
1639
|
+
logger.debug(
|
|
1640
|
+
"[MFE Config] Config loaded from VC_MICROFRONTENDS_CONFIG:",
|
|
1641
|
+
maybeConfigFromEnv
|
|
1642
|
+
);
|
|
1518
1643
|
return MicrofrontendsServer.fromFile({
|
|
1519
1644
|
filePath: maybeConfigFromEnv,
|
|
1520
1645
|
cookies
|
|
1521
1646
|
});
|
|
1522
1647
|
}
|
|
1523
1648
|
} else {
|
|
1649
|
+
const vercelDir = join3(packageRoot, ".vercel");
|
|
1650
|
+
logger.debug(
|
|
1651
|
+
"[MFE Config] Searching for config in .vercel directory:",
|
|
1652
|
+
vercelDir
|
|
1653
|
+
);
|
|
1524
1654
|
const maybeConfigFromVercel = findConfig({
|
|
1525
|
-
dir:
|
|
1655
|
+
dir: vercelDir,
|
|
1526
1656
|
customConfigFilename
|
|
1527
1657
|
});
|
|
1528
1658
|
if (maybeConfigFromVercel) {
|
|
1659
|
+
logger.debug(
|
|
1660
|
+
"[MFE Config] Config found in .vercel directory:",
|
|
1661
|
+
maybeConfigFromVercel
|
|
1662
|
+
);
|
|
1529
1663
|
return MicrofrontendsServer.fromFile({
|
|
1530
1664
|
filePath: maybeConfigFromVercel,
|
|
1531
1665
|
cookies
|
|
1532
1666
|
});
|
|
1533
1667
|
}
|
|
1534
1668
|
if (isMonorepo2) {
|
|
1669
|
+
logger.debug(
|
|
1670
|
+
"[MFE Config] Inferring microfrontends location in monorepo for application:",
|
|
1671
|
+
applicationContext.name
|
|
1672
|
+
);
|
|
1535
1673
|
const defaultPackage = inferMicrofrontendsLocation({
|
|
1536
1674
|
repositoryRoot,
|
|
1537
1675
|
applicationContext,
|
|
1538
1676
|
customConfigFilename
|
|
1539
1677
|
});
|
|
1678
|
+
logger.debug(
|
|
1679
|
+
"[MFE Config] Inferred package location:",
|
|
1680
|
+
defaultPackage
|
|
1681
|
+
);
|
|
1540
1682
|
const maybeConfigFromDefault = findConfig({
|
|
1541
1683
|
dir: defaultPackage,
|
|
1542
1684
|
customConfigFilename
|
|
1543
1685
|
});
|
|
1544
1686
|
if (maybeConfigFromDefault) {
|
|
1687
|
+
logger.debug(
|
|
1688
|
+
"[MFE Config] Config found in inferred package:",
|
|
1689
|
+
maybeConfigFromDefault
|
|
1690
|
+
);
|
|
1545
1691
|
return MicrofrontendsServer.fromFile({
|
|
1546
1692
|
filePath: maybeConfigFromDefault,
|
|
1547
1693
|
cookies
|
|
1548
1694
|
});
|
|
1549
1695
|
}
|
|
1696
|
+
logger.debug("[MFE Config] No config found in inferred package");
|
|
1550
1697
|
}
|
|
1551
1698
|
}
|
|
1552
1699
|
throw new MicrofrontendError(
|
|
@@ -1572,8 +1719,13 @@ var MicrofrontendsServer = class {
|
|
|
1572
1719
|
cookies
|
|
1573
1720
|
}) {
|
|
1574
1721
|
try {
|
|
1722
|
+
logger.debug("[MFE Config] Reading config from file:", filePath);
|
|
1575
1723
|
const configJson = fs6.readFileSync(filePath, "utf-8");
|
|
1576
1724
|
const config = MicrofrontendsServer.validate(configJson);
|
|
1725
|
+
logger.debug(
|
|
1726
|
+
"[MFE Config] Config loaded with applications:",
|
|
1727
|
+
Object.keys(config.applications)
|
|
1728
|
+
);
|
|
1577
1729
|
return new MicrofrontendsServer({
|
|
1578
1730
|
config,
|
|
1579
1731
|
overrides: cookies ? parseOverrides(cookies) : void 0
|