@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
229 lines (201 loc) • 6.45 kB
text/typescript
import * as fs from "node:fs/promises";
import { CREDENTIALS_DIR } from "./credentials";
import path from "node:path";
import ora from "ora";
export const checkNodeVersion = async () => {
const spinner = ora("Checking node version...").start();
const [major, minor] = process.versions.node.split(".").map(Number);
let packageJson;
try {
packageJson = JSON.parse(
await fs.readFile(
path.join(import.meta.dirname, "../package.json"),
"utf-8",
),
);
} catch (e) {
throw new Error("Failed to read package.json of core-sdk");
}
const {
engines: { node },
} = packageJson;
const [minMajor, minMinor] = node
.split(".")
.map((v: string) => v.replace(/[^\d]/g, ""))
.map(Number);
if (major < minMajor || (major === minMajor && minor < minMinor)) {
spinner.fail(
`Node version ${minMajor}.${minMinor} or higher is required. You are running ${major}.${minor}.`,
);
process.exit(1);
} else {
spinner.stop();
return true;
}
};
/**
* Get the value of a process argument by key
* Example: getArgumentByKey("--email") or getArgumentByKey(["--email", "-e"])
* @param key The key to search for in the process arguments
* @returns
*/
export const getArgumentByKey = (key: string | string[]) => {
if (Array.isArray(key)) {
for (const k of key) {
if (process.argv.includes(k)) {
const index = process.argv.indexOf(k);
return index !== -1 ? process.argv[index + 1] : undefined;
}
}
return undefined;
}
const index = process.argv.indexOf(key);
return index !== -1 ? process.argv[index + 1] : undefined;
};
export const SUCCESS_FLAG_FILE = `${CREDENTIALS_DIR}/success`;
/**
* Store a flag in the credentials directory to indicate a successful build
* This is used to determine if the build was successful or not
*/
export const storeBuildSuccessFlag = async () => {
try {
await fs.access(CREDENTIALS_DIR);
} catch (_e) {
await fs.mkdir(CREDENTIALS_DIR);
}
await fs.writeFile(SUCCESS_FLAG_FILE, "true");
};
/**
* Remove the success flag from the credentials directory
*/
export const removeBuildSuccessFlag = async () => {
try {
await fs.unlink(SUCCESS_FLAG_FILE);
} catch (_e) {}
};
/**
* Check if the build was successful
*/
export const checkBuildSuccess = async () => {
try {
await fs.access(SUCCESS_FLAG_FILE);
return true;
} catch (_e) {
return false;
}
};
const getPackageVersion = async (packageName: string) => {
const packageJsonPath = path.join(
process.cwd(),
"node_modules",
packageName,
"package.json",
);
let packageJson;
try {
packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
return packageJson.version;
} catch (e) {
return undefined;
}
};
/**
* Attempts to resolve a local file reference to get the actual package version
* @param packageName The name of the package
* @param filePath The file path reference (e.g. "file:../packages/core")
* @returns The resolved version or "local-dev" if not found
*/
const resolveLocalFileVersion = async (
packageName: string,
filePath: string,
) => {
try {
// Remove the file: prefix and resolve the path
const refPath = filePath.replace(/^file:/, "");
const absPath = path.resolve(process.cwd(), refPath, "package.json");
// Read the package.json from the referenced path
const refPackageJson = JSON.parse(await fs.readFile(absPath, "utf-8"));
return refPackageJson.version || "local-dev";
} catch (e) {
console.warn(`Failed to resolve local version for ${packageName}`, e);
return "local-dev";
}
};
export const getSDKVersions = async () => {
const packageNames = [
"@embeddable.com/core",
"@embeddable.com/react",
"@embeddable.com/sdk-core",
"@embeddable.com/sdk-react",
"@embeddable.com/sdk-utils",
];
// First try to get versions from node_modules
const sdkVersions = await packageNames.reduce<
Promise<Record<string, string>>
>(
async (accPromise, packageName) => {
const acc = await accPromise; // Wait for the previous accumulator to resolve
const version = await getPackageVersion(packageName);
if (version) {
acc[packageName] = version;
}
return acc;
},
Promise.resolve({}), // Start with a resolved promise containing an empty object
);
// If no versions were found, try to get them from package.json dependencies/devDependencies
if (
Object.keys(sdkVersions).length === 0 &&
process.env.NODE_ENV !== "test"
) {
try {
const packageJsonPath = path.join(process.cwd(), "package.json");
const packageJson = JSON.parse(
await fs.readFile(packageJsonPath, "utf-8"),
);
const { dependencies = {}, devDependencies = {} } = packageJson;
const allDeps = { ...dependencies, ...devDependencies };
for (const packageName of packageNames) {
if (allDeps[packageName]) {
// For file: references, try to get the actual version from the referenced package
if (allDeps[packageName].startsWith("file:")) {
sdkVersions[packageName] = await resolveLocalFileVersion(
packageName,
allDeps[packageName],
);
} else {
sdkVersions[packageName] = allDeps[packageName];
}
}
}
} catch (e) {
console.warn("Failed to read package.json for SDK versions", e);
}
}
// If we're in a test environment and still have no versions, add fallback values
if (Object.keys(sdkVersions).length === 0) {
const isTestEnv = process.cwd().includes("test-sdk");
if (isTestEnv) {
console.warn("Test environment detected, using fallback SDK versions");
packageNames.forEach((pkg) => {
sdkVersions[pkg] = "local-dev";
});
}
}
return sdkVersions;
};
export const hrtimeToISO8601 = (
hrtime: number[] | null | undefined,
): String => {
if (hrtime === null || hrtime === undefined) {
return "";
}
const seconds = hrtime[0];
const nanoseconds = hrtime[1];
// Convert time components
const totalSeconds = seconds + nanoseconds / 1e9;
const minutes = Math.floor(totalSeconds / 60);
const remainingSeconds = totalSeconds % 60;
// Format ISO 8601 duration without hours
return `PT${minutes > 0 ? minutes + "M" : ""}${remainingSeconds.toFixed(3)}S`;
};