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