@vercel/microfrontends
Version:
Defines configuration and utilities for microfrontends development
197 lines (192 loc) • 6.07 kB
JavaScript
// src/config/microfrontends/utils/find-config.ts
import fs from "node:fs";
import { join } from "node:path";
// src/config/constants.ts
var CONFIGURATION_FILENAMES = [
"microfrontends.jsonc",
"microfrontends.json"
];
// src/config/microfrontends/utils/find-config.ts
function findConfig({ dir }) {
for (const filename of CONFIGURATION_FILENAMES) {
const maybeConfig = join(dir, filename);
if (fs.existsSync(maybeConfig)) {
return maybeConfig;
}
}
return null;
}
// src/config/microfrontends/utils/infer-microfrontends-location.ts
import { dirname } from "node:path";
import { readFileSync } from "node:fs";
import { parse } from "jsonc-parser";
import fg from "fast-glob";
// src/config/errors.ts
var MicrofrontendError = class extends Error {
constructor(message, opts) {
super(message, { cause: opts?.cause });
this.name = "MicrofrontendsError";
this.source = opts?.source ?? "@vercel/microfrontends";
this.type = opts?.type ?? "unknown";
this.subtype = opts?.subtype;
Error.captureStackTrace(this, MicrofrontendError);
}
isKnown() {
return this.type !== "unknown";
}
isUnknown() {
return !this.isKnown();
}
/**
* Converts an error to a MicrofrontendsError.
* @param original - The original error to convert.
* @returns The converted MicrofrontendsError.
*/
static convert(original, opts) {
if (opts?.fileName) {
const err = MicrofrontendError.convertFSError(original, opts.fileName);
if (err) {
return err;
}
}
if (original.message.includes(
"Code generation from strings disallowed for this context"
)) {
return new MicrofrontendError(original.message, {
type: "config",
subtype: "unsupported_validation_env",
source: "ajv"
});
}
return new MicrofrontendError(original.message);
}
static convertFSError(original, fileName) {
if (original instanceof Error && "code" in original) {
if (original.code === "ENOENT") {
return new MicrofrontendError(`Could not find "${fileName}"`, {
type: "config",
subtype: "unable_to_read_file",
source: "fs"
});
}
if (original.code === "EACCES") {
return new MicrofrontendError(
`Permission denied while accessing "${fileName}"`,
{
type: "config",
subtype: "invalid_permissions",
source: "fs"
}
);
}
}
if (original instanceof SyntaxError) {
return new MicrofrontendError(
`Failed to parse "${fileName}": Invalid JSON format.`,
{
type: "config",
subtype: "invalid_syntax",
source: "fs"
}
);
}
return null;
}
/**
* Handles an unknown error and returns a MicrofrontendsError instance.
* @param err - The error to handle.
* @returns A MicrofrontendsError instance.
*/
static handle(err, opts) {
if (err instanceof MicrofrontendError) {
return err;
}
if (err instanceof Error) {
return MicrofrontendError.convert(err, opts);
}
if (typeof err === "object" && err !== null) {
if ("message" in err && typeof err.message === "string") {
return MicrofrontendError.convert(new Error(err.message), opts);
}
}
return new MicrofrontendError("An unknown error occurred");
}
};
// src/config/microfrontends/utils/infer-microfrontends-location.ts
var configCache = {};
function findPackageWithMicrofrontendsConfig({
repositoryRoot,
applicationName
}) {
try {
const microfrontendsJsonPaths = fg.globSync(
`**/{${CONFIGURATION_FILENAMES.join(",")}}`,
{
cwd: repositoryRoot,
absolute: true,
onlyFiles: true,
followSymbolicLinks: false,
ignore: ["**/node_modules/**", "**/.git/**"]
}
);
const matchingPaths = [];
for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
try {
const microfrontendsJsonContent = readFileSync(
microfrontendsJsonPath,
"utf-8"
);
const microfrontendsJson = parse(microfrontendsJsonContent);
if (microfrontendsJson.applications[applicationName]) {
matchingPaths.push(microfrontendsJsonPath);
} else {
for (const [_, app] of Object.entries(
microfrontendsJson.applications
)) {
if (app.packageName === applicationName) {
matchingPaths.push(microfrontendsJsonPath);
}
}
}
} catch (error) {
}
}
if (matchingPaths.length > 1) {
throw new MicrofrontendError(
`Found multiple \`microfrontends.json\` files in the repository referencing the application "${applicationName}", but only one is allowed.
${matchingPaths.join("\n \u2022 ")}`,
{ type: "config", subtype: "inference_failed" }
);
}
if (matchingPaths.length === 0) {
throw new MicrofrontendError(
`Could not find a \`microfrontends.json\` file in the repository that contains "applications.${applicationName}". Microfrontends defined in separate repositories are not supported yet.`,
{ type: "config", subtype: "inference_failed" }
);
}
const [packageJsonPath] = matchingPaths;
return dirname(packageJsonPath);
} catch (error) {
return null;
}
}
function inferMicrofrontendsLocation(opts) {
const cacheKey = `${opts.repositoryRoot}-${opts.applicationName}`;
if (configCache[cacheKey]) {
return configCache[cacheKey];
}
const result = findPackageWithMicrofrontendsConfig(opts);
if (!result) {
throw new MicrofrontendError(
`Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationName}" starting in directory "${opts.repositoryRoot}".`,
{ type: "config", subtype: "inference_failed" }
);
}
configCache[cacheKey] = result;
return result;
}
export {
findConfig,
inferMicrofrontendsLocation
};
//# sourceMappingURL=utils.js.map