@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
1,292 lines (1,273 loc) • 748 kB
JavaScript
import * as fs from 'node:fs/promises';
import { readdir, lstat, readFile } from 'node:fs/promises';
import * as path$1 from 'node:path';
import path__default, { join } from 'node:path';
import * as vite from 'vite';
import ora from 'ora';
import 'node:child_process';
import * as crypto from 'node:crypto';
import * as path from 'path';
import path__default$1, { basename } from 'path';
import fg from 'fast-glob';
import * as fsSync from 'node:fs';
import { existsSync } from 'node:fs';
import { createNodeLogger, createNodeSys } from '@stencil/core/sys/node';
import { loadConfig, createCompiler } from '@stencil/core/compiler';
import * as sorcery from 'sorcery';
import * as os from 'node:os';
import * as YAML from 'yaml';
import { MEASURE_TYPES, DIMENSION_TYPES } from '@embeddable.com/core';
import * as url from 'node:url';
import require$$4$1 from 'util';
import require$$1 from 'os';
import require$$3 from 'http';
import require$$4 from 'https';
import require$$0$1 from 'url';
import require$$2$1, { createReadStream } from 'fs';
import axios from 'axios';
import archiver from 'archiver';
import { select } from '@inquirer/prompts';
import open from 'open';
import * as http from 'node:http';
import { WebSocketServer } from 'ws';
import * as chokidar from 'chokidar';
import minimist from 'minimist';
import * as dotenv from 'dotenv';
import finalhandler from 'finalhandler';
import serveStatic from 'serve-static';
import { stat } from 'fs/promises';
var findFiles = async (initialSrcDir, regex) => {
const filesList = [];
async function findFilesRec(srcDir) {
const allFiles = await readdir(srcDir);
for (const file of allFiles) {
const filePath = join(srcDir, file);
const status = await lstat(filePath);
if (status.isDirectory()) {
await findFilesRec(filePath);
}
const fileName = file.match(regex);
if (fileName) {
filesList.push([fileName[1], filePath]);
}
}
}
await findFilesRec(initialSrcDir);
return filesList;
};
var util$2;
(function (util) {
util.assertEqual = (val) => val;
function assertIs(_arg) { }
util.assertIs = assertIs;
function assertNever(_x) {
throw new Error();
}
util.assertNever = assertNever;
util.arrayToEnum = (items) => {
const obj = {};
for (const item of items) {
obj[item] = item;
}
return obj;
};
util.getValidEnumValues = (obj) => {
const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
const filtered = {};
for (const k of validKeys) {
filtered[k] = obj[k];
}
return util.objectValues(filtered);
};
util.objectValues = (obj) => {
return util.objectKeys(obj).map(function (e) {
return obj[e];
});
};
util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban
? (obj) => Object.keys(obj) // eslint-disable-line ban/ban
: (object) => {
const keys = [];
for (const key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
keys.push(key);
}
}
return keys;
};
util.find = (arr, checker) => {
for (const item of arr) {
if (checker(item))
return item;
}
return undefined;
};
util.isInteger = typeof Number.isInteger === "function"
? (val) => Number.isInteger(val) // eslint-disable-line ban/ban
: (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val;
function joinValues(array, separator = " | ") {
return array
.map((val) => (typeof val === "string" ? `'${val}'` : val))
.join(separator);
}
util.joinValues = joinValues;
util.jsonStringifyReplacer = (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
})(util$2 || (util$2 = {}));
var objectUtil$1;
(function (objectUtil) {
objectUtil.mergeShapes = (first, second) => {
return {
...first,
...second, // second overwrites first
};
};
})(objectUtil$1 || (objectUtil$1 = {}));
util$2.arrayToEnum([
"string",
"nan",
"number",
"integer",
"float",
"boolean",
"date",
"bigint",
"symbol",
"function",
"undefined",
"null",
"array",
"object",
"unknown",
"promise",
"void",
"never",
"map",
"set",
]);
const ZodIssueCode$1 = util$2.arrayToEnum([
"invalid_type",
"invalid_literal",
"custom",
"invalid_union",
"invalid_union_discriminator",
"invalid_enum_value",
"unrecognized_keys",
"invalid_arguments",
"invalid_return_type",
"invalid_date",
"invalid_string",
"too_small",
"too_big",
"invalid_intersection_types",
"not_multiple_of",
"not_finite",
]);
let ZodError$1 = class ZodError extends Error {
get errors() {
return this.issues;
}
constructor(issues) {
super();
this.issues = [];
this.addIssue = (sub) => {
this.issues = [...this.issues, sub];
};
this.addIssues = (subs = []) => {
this.issues = [...this.issues, ...subs];
};
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) {
// eslint-disable-next-line ban/ban
Object.setPrototypeOf(this, actualProto);
}
else {
this.__proto__ = actualProto;
}
this.name = "ZodError";
this.issues = issues;
}
format(_mapper) {
const mapper = _mapper ||
function (issue) {
return issue.message;
};
const fieldErrors = { _errors: [] };
const processError = (error) => {
for (const issue of error.issues) {
if (issue.code === "invalid_union") {
issue.unionErrors.map(processError);
}
else if (issue.code === "invalid_return_type") {
processError(issue.returnTypeError);
}
else if (issue.code === "invalid_arguments") {
processError(issue.argumentsError);
}
else if (issue.path.length === 0) {
fieldErrors._errors.push(mapper(issue));
}
else {
let curr = fieldErrors;
let i = 0;
while (i < issue.path.length) {
const el = issue.path[i];
const terminal = i === issue.path.length - 1;
if (!terminal) {
curr[el] = curr[el] || { _errors: [] };
// if (typeof el === "string") {
// curr[el] = curr[el] || { _errors: [] };
// } else if (typeof el === "number") {
// const errorArray: any = [];
// errorArray._errors = [];
// curr[el] = curr[el] || errorArray;
// }
}
else {
curr[el] = curr[el] || { _errors: [] };
curr[el]._errors.push(mapper(issue));
}
curr = curr[el];
i++;
}
}
}
};
processError(this);
return fieldErrors;
}
static assert(value) {
if (!(value instanceof ZodError)) {
throw new Error(`Not a ZodError: ${value}`);
}
}
toString() {
return this.message;
}
get message() {
return JSON.stringify(this.issues, util$2.jsonStringifyReplacer, 2);
}
get isEmpty() {
return this.issues.length === 0;
}
flatten(mapper = (issue) => issue.message) {
const fieldErrors = {};
const formErrors = [];
for (const sub of this.issues) {
if (sub.path.length > 0) {
fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
fieldErrors[sub.path[0]].push(mapper(sub));
}
else {
formErrors.push(mapper(sub));
}
}
return { formErrors, fieldErrors };
}
get formErrors() {
return this.flatten();
}
};
ZodError$1.create = (issues) => {
const error = new ZodError$1(issues);
return error;
};
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var errorUtil$1;
(function (errorUtil) {
errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {};
errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message;
})(errorUtil$1 || (errorUtil$1 = {}));
var ZodFirstPartyTypeKind$1;
(function (ZodFirstPartyTypeKind) {
ZodFirstPartyTypeKind["ZodString"] = "ZodString";
ZodFirstPartyTypeKind["ZodNumber"] = "ZodNumber";
ZodFirstPartyTypeKind["ZodNaN"] = "ZodNaN";
ZodFirstPartyTypeKind["ZodBigInt"] = "ZodBigInt";
ZodFirstPartyTypeKind["ZodBoolean"] = "ZodBoolean";
ZodFirstPartyTypeKind["ZodDate"] = "ZodDate";
ZodFirstPartyTypeKind["ZodSymbol"] = "ZodSymbol";
ZodFirstPartyTypeKind["ZodUndefined"] = "ZodUndefined";
ZodFirstPartyTypeKind["ZodNull"] = "ZodNull";
ZodFirstPartyTypeKind["ZodAny"] = "ZodAny";
ZodFirstPartyTypeKind["ZodUnknown"] = "ZodUnknown";
ZodFirstPartyTypeKind["ZodNever"] = "ZodNever";
ZodFirstPartyTypeKind["ZodVoid"] = "ZodVoid";
ZodFirstPartyTypeKind["ZodArray"] = "ZodArray";
ZodFirstPartyTypeKind["ZodObject"] = "ZodObject";
ZodFirstPartyTypeKind["ZodUnion"] = "ZodUnion";
ZodFirstPartyTypeKind["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
ZodFirstPartyTypeKind["ZodIntersection"] = "ZodIntersection";
ZodFirstPartyTypeKind["ZodTuple"] = "ZodTuple";
ZodFirstPartyTypeKind["ZodRecord"] = "ZodRecord";
ZodFirstPartyTypeKind["ZodMap"] = "ZodMap";
ZodFirstPartyTypeKind["ZodSet"] = "ZodSet";
ZodFirstPartyTypeKind["ZodFunction"] = "ZodFunction";
ZodFirstPartyTypeKind["ZodLazy"] = "ZodLazy";
ZodFirstPartyTypeKind["ZodLiteral"] = "ZodLiteral";
ZodFirstPartyTypeKind["ZodEnum"] = "ZodEnum";
ZodFirstPartyTypeKind["ZodEffects"] = "ZodEffects";
ZodFirstPartyTypeKind["ZodNativeEnum"] = "ZodNativeEnum";
ZodFirstPartyTypeKind["ZodOptional"] = "ZodOptional";
ZodFirstPartyTypeKind["ZodNullable"] = "ZodNullable";
ZodFirstPartyTypeKind["ZodDefault"] = "ZodDefault";
ZodFirstPartyTypeKind["ZodCatch"] = "ZodCatch";
ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise";
ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded";
ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline";
ZodFirstPartyTypeKind["ZodReadonly"] = "ZodReadonly";
})(ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {}));
const errorFormatter = (issues) => {
const errors = [];
for (let issue of issues) {
if (issue.code === ZodIssueCode$1.invalid_union && issue.unionErrors.length) {
const error = issue.unionErrors[issue.unionErrors.length - 1];
issue = error.issues[error.issues.length - 1];
}
const path = formatErrorPath(issue.path);
const message = issue.message;
errors.push(`"${path}": ${message}`);
}
return errors;
};
const formatErrorPath = (path) => {
let formatted = "";
for (const pathElement of path) {
if (formatted.length === 0) {
formatted = `${pathElement}`;
}
else {
formatted +=
typeof pathElement === "number"
? `[${pathElement}]`
: `.${pathElement}`;
}
}
return formatted;
};
/**
* Get the hash of the content string. It returns the first 5 characters of the hash
* Example: getContentHash("Hello World")
* @param contentString The content string to hash
* @returns
*/
const getContentHash = (contentString) => {
return crypto
.createHash("md5")
.update(contentString)
.digest("hex")
.substring(0, 5);
};
const loadJson = async (filePath) => {
const data = await readFile(filePath, "utf-8");
return JSON.parse(data);
};
const getComponentLibraryConfig = (componentLibrary) => {
let libraryName = componentLibrary;
let include;
const exclude = [];
if (typeof componentLibrary === "object" && componentLibrary !== null) {
libraryName = componentLibrary.name;
if (componentLibrary.include) {
include = [...componentLibrary.include];
}
if (componentLibrary.exclude) {
exclude.push(...componentLibrary.exclude);
}
}
return { libraryName, include, exclude };
};
const EXTERNAL_LIBRARY_GLOBAL_HOOKS_META_NAME = "globalHooks.json";
const getGlobalHooksMeta = async (ctx, libraryName) => {
return (await loadJson(getLibraryPath(ctx, libraryName, EXTERNAL_LIBRARY_GLOBAL_HOOKS_META_NAME)));
};
const getLibraryPath = (ctx, libraryName, fileName) => path.resolve(ctx.client.rootDir, "node_modules", libraryName, "dist", fileName);
const EMB_TYPE_FILE_REGEX = /^(.*)\.type\.emb\.[jt]s$/;
const EMB_OPTIONS_FILE_REGEX = /^(.*)\.options\.emb\.[jt]s$/;
var buildTypes = async (ctx) => {
const progress = ora("Building types...").start();
await generate$1(ctx);
await build$1(ctx);
await cleanup$1(ctx);
progress.succeed("Types built completed");
};
async function generate$1(ctx) {
const typeFiles = await findFiles(ctx.client.srcDir, EMB_TYPE_FILE_REGEX);
const optionsFiles = await findFiles(ctx.client.srcDir, EMB_OPTIONS_FILE_REGEX);
const additionalImports = await getAdditionalImportsFromInstalledLibraries(ctx);
const repositoryTypeImports = typeFiles
.concat(optionsFiles)
.map(([_fileName, filePath]) => `import '../${path$1
.relative(ctx.client.rootDir, filePath)
.replaceAll("\\", "/")}';`)
.join("\n");
const typeImports = additionalImports.join("\n") + repositoryTypeImports;
await fs.writeFile(path$1.resolve(ctx.client.buildDir, ctx.outputOptions.typesEntryPointFilename), typeImports);
}
async function build$1(ctx) {
var _a, _b, _c, _d;
const typesFilePath = path$1.resolve(ctx.client.buildDir, ctx.outputOptions.typesEntryPointFilename);
await vite.build({
logLevel: "error",
build: {
emptyOutDir: false,
sourcemap: ((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch) ? false : true, // No sourcemaps for types in dev
minify: !((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.watch), // No minification in dev
rollupOptions: ((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch) ? { treeshake: false } : undefined,
lib: {
entry: typesFilePath,
formats: ["es"],
fileName: "embeddable-types",
},
outDir: ctx.client.buildDir,
},
});
if (!((_d = ctx.dev) === null || _d === void 0 ? void 0 : _d.watch)) {
const fileContent = await fs.readFile(typesFilePath, "utf8");
const fileHash = getContentHash(fileContent);
const fileName = `embeddable-types-${fileHash}.js`;
await fs.rename(path$1.resolve(ctx.client.buildDir, "embeddable-types.js"), path$1.resolve(ctx.client.buildDir, fileName));
}
}
async function cleanup$1(ctx) {
await fs.rm(path$1.resolve(ctx.client.buildDir, "embeddable-types-entry-point.js"));
}
async function getAdditionalImportsFromInstalledLibraries(ctx) {
const componentLibraries = ctx.client.componentLibraries;
const additionalImports = [];
for (const componentLibrary of componentLibraries) {
const { libraryName } = getComponentLibraryConfig(componentLibrary);
try {
fg.sync(path$1.resolve(ctx.client.rootDir, "node_modules", libraryName, "dist", "embeddable-types-*.js")).forEach((file) => {
const fileName = path$1.basename(file);
additionalImports.push(`import '${libraryName}/dist/${fileName}';`);
});
}
catch (e) {
console.error(`Can't load component library: ${libraryName}`, e);
throw e;
}
}
return additionalImports;
}
const TEMP_JS_HOOK_FILE = "embeddableThemeHook.js";
const LIFECYCLE_OUTPUT_NAME = "embeddable-lifecycle";
const THEME_PROVIDER_OUTPUT_NAME = "embeddable-theme";
var buildGlobalHooks = async (ctx) => {
var _a;
const watch = (_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch;
const progress = watch ? undefined : ora("Building global hooks...").start();
try {
await fs.mkdir(ctx.client.tmpDir, { recursive: true });
const { fileName: themeProvider, watcher: themeWatcher } = await buildThemeHook(ctx);
const { lifecycleHooks, watcher: lifecycleWatcher } = await buildLifecycleHooks(ctx);
await saveGlobalHooksMeta(ctx, themeProvider, lifecycleHooks);
progress === null || progress === void 0 ? void 0 : progress.succeed("Global hooks build completed");
return { themeWatcher, lifecycleWatcher };
}
catch (error) {
progress === null || progress === void 0 ? void 0 : progress.fail("Global hooks build failed");
throw error;
}
};
/**
* Build theme hooks for a given component library.
*/
async function buildThemeHook(ctx) {
var _a, _b, _c;
const componentLibraries = ctx.client.componentLibraries;
const repoThemeHookExists = existsSync(ctx.client.customizationFile);
const imports = [];
const functionNames = [];
for (let i = 0; i < componentLibraries.length; i++) {
const libraryConfig = componentLibraries[i];
const { libraryName } = getComponentLibraryConfig(libraryConfig);
const libMeta = await getGlobalHooksMeta(ctx, libraryName);
const themeProvider = libMeta.themeProvider;
if (!themeProvider)
continue;
// Prepare imports: library theme + repo theme (if exists)
const functionName = `libraryThemeProvider${i}`;
const libraryThemeImport = `import ${functionName} from '${libraryName}/dist/${themeProvider}'`;
functionNames.push(functionName);
imports.push(libraryThemeImport);
}
if (!imports.length && !repoThemeHookExists) {
return { fileName: undefined, watcher: undefined };
}
const repoThemeImport = repoThemeHookExists
? `import localThemeProvider from '${ctx.client.customizationFile.replace(/\\/g, "/")}';`
: "const localThemeProvider = () => {};";
// Generate a temporary file that imports both library and repo theme
await generateTemporaryHookFile(ctx, imports, functionNames, repoThemeImport);
// Build the temporary file with Vite
const buildResults = await buildWithVite(ctx, getTempHookFilePath(ctx), THEME_PROVIDER_OUTPUT_NAME, (_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch, !((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.watch));
// Cleanup temporary file
if (!((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch)) {
await cleanupTemporaryHookFile(ctx);
}
return buildResults;
}
/**
* Build theme hooks for a given component library.
*/
async function buildLifecycleHooks(ctx) {
var _a, _b;
const componentLibraries = ctx.client.componentLibraries;
const builtLifecycleHooks = [];
const repoLifecycleExist = existsSync(ctx.client.lifecycleHooksFile);
let lifecycleWatcher = undefined;
// If lifecycle exists, build it right away to get the hashed output
if (repoLifecycleExist) {
const { fileName: repoLifecycleFileName, watcher } = await buildWithVite(ctx, ctx.client.lifecycleHooksFile, LIFECYCLE_OUTPUT_NAME, (_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch, false);
if ((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.watch) {
lifecycleWatcher = watcher;
}
builtLifecycleHooks.push(repoLifecycleFileName);
}
for (const libraryConfig of componentLibraries) {
const { libraryName } = getComponentLibraryConfig(libraryConfig);
const libMeta = await getGlobalHooksMeta(ctx, libraryName);
const lifecycleHooks = libMeta.lifecycleHooks;
for (const lifecycleHook of lifecycleHooks) {
const libLifecycleHook = path$1.resolve(ctx.client.rootDir, "node_modules", libraryName, "dist", lifecycleHook);
const { fileName: lifecycleHookFileName } = await buildWithVite(ctx, libLifecycleHook, LIFECYCLE_OUTPUT_NAME);
builtLifecycleHooks.push(lifecycleHookFileName);
}
}
return { lifecycleHooks: builtLifecycleHooks, watcher: lifecycleWatcher };
}
/**
* Write the final global hooks metadata to disk (themeHooksMeta, lifecycleHookMeta).
*/
async function saveGlobalHooksMeta(ctx, themeProvider, lifecycleHooks) {
const metaFilePath = path$1.resolve(ctx.client.buildDir, EXTERNAL_LIBRARY_GLOBAL_HOOKS_META_NAME);
const data = JSON.stringify({ themeProvider, lifecycleHooks }, null, 2);
fsSync.writeFileSync(metaFilePath, data);
}
/**
* Generate a temporary file which imports the library theme and repository theme,
* replacing template placeholders.
*/
async function generateTemporaryHookFile(ctx, libraryThemeImports, functionNames, repoThemeImport) {
const templatePath = path$1.resolve(ctx.core.templatesDir, "embeddableThemeHook.js.template");
const templateContent = await fs.readFile(templatePath, "utf8");
const newContent = templateContent
.replace("{{LIBRARY_THEME_IMPORTS}}", libraryThemeImports.join("\n"))
.replace("{{ARRAY_OF_LIBRARY_THEME_PROVIDERS}}", functionNames.join("\n"))
.replace("{{LOCAL_THEME_IMPORT}}", repoThemeImport);
// Write to temporary hook file
await fs.writeFile(getTempHookFilePath(ctx), newContent, "utf8");
}
/**
* Build a file with Vite and return the hashed output file name (e.g., embeddable-theme-xxxx.js).
*/
async function buildWithVite(ctx, entryFile, outputFile, watch = false, useHash = true) {
const fileContent = await fs.readFile(entryFile, "utf8");
const fileHash = getContentHash(fileContent);
// Bundle using Vite
const fileName = useHash ? `${outputFile}-${fileHash}` : outputFile;
const fileWatcher = await vite.build({
logLevel: watch ? "info" : "error",
build: {
emptyOutDir: false,
lib: {
entry: entryFile,
formats: ["es"],
fileName: fileName,
},
outDir: ctx.client.buildDir,
watch: watch ? {} : undefined,
},
});
if (watch) {
await waitForInitialBuild(fileWatcher);
}
const watcher = watch
? fileWatcher
: undefined;
return { fileName: `${fileName}.js`, watcher };
}
/**
* Remove the temporary hook file after building.
*/
async function cleanupTemporaryHookFile(ctx) {
await fs.rm(getTempHookFilePath(ctx), { force: true });
}
/**
* Get the path to the temporary hook file in the build directory.
*/
function getTempHookFilePath(ctx) {
return path$1.resolve(ctx.client.tmpDir, TEMP_JS_HOOK_FILE);
}
function waitForInitialBuild(watcher) {
return new Promise((resolve, reject) => {
function onEvent(event) {
if (event.code === "END") {
watcher.off("event", onEvent);
resolve();
}
else if (event.code === "ERROR") {
watcher.off("event", onEvent);
reject(event.error);
}
}
watcher.on("event", onEvent);
});
}
var prepare$1 = async (ctx) => {
await removeIfExists(ctx);
await copyStencilConfigsToClient(ctx);
await createComponentDir(ctx.client.componentDir);
};
async function removeIfExists(ctx) {
const promises = [];
if (fsSync.existsSync(ctx.client.buildDir)) {
promises.push(fs.rm(ctx.client.buildDir, { recursive: true }));
}
if (fsSync.existsSync(ctx.client.tmpDir)) {
promises.push(fs.rm(ctx.client.tmpDir, { recursive: true }));
}
await Promise.all(promises);
}
async function copyStencilConfigsToClient(ctx) {
await fs.cp(ctx.core.configsDir, ctx.client.webComponentRoot, {
recursive: true,
});
}
async function createComponentDir(dir) {
await fs.mkdir(dir, { recursive: true });
}
const STYLE_IMPORTS_TOKEN = "{{STYLES_IMPORT}}";
const RENDER_IMPORT_TOKEN = "{{RENDER_IMPORT}}";
// stencil doesn't support dynamic component tag name, so we need to replace it manually
const COMPONENT_TAG_TOKEN = "replace-this-with-component-name";
var generate = async (ctx, pluginName) => {
await injectCSS(ctx, pluginName);
await injectBundleRender(ctx, pluginName);
await runStencil(ctx);
await generateSourceMap(ctx, pluginName);
};
async function injectCSS(ctx, pluginName) {
const CUSTOMER_BUILD = path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName);
const allFiles = await fs.readdir(CUSTOMER_BUILD);
const imports = allFiles
.filter((fileName) => fileName.endsWith(".css"))
.map((fileName) => `@import '../../${ctx[pluginName].outputOptions.buildName}/${fileName}';`);
const componentLibraries = ctx.client.componentLibraries;
for (const componentLibrary of componentLibraries) {
const { libraryName } = getComponentLibraryConfig(componentLibrary);
const allLibFiles = await fs.readdir(path$1.resolve(ctx.client.rootDir, "node_modules", libraryName, "dist"));
allLibFiles
.filter((fileName) => fileName.endsWith(".css"))
.forEach((fileName) => imports.push(`@import '~${libraryName}/dist/${fileName}';`));
}
const cssFilesImportsStr = imports.join("\n");
const content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "style.css.template"), "utf8");
await fs.writeFile(path$1.resolve(ctx.client.componentDir, "style.css"), content.replace(STYLE_IMPORTS_TOKEN, cssFilesImportsStr));
}
async function injectBundleRender(ctx, pluginName) {
var _a;
const importStr = `import render from '../../${ctx[pluginName].outputOptions.buildName}/${ctx[pluginName].outputOptions.fileName}';`;
let content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "component.tsx.template"), "utf8");
if (!!((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch)) {
content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
}
await fs.writeFile(path$1.resolve(ctx.client.componentDir, "component.tsx"), content.replace(RENDER_IMPORT_TOKEN, importStr));
}
async function addComponentTagName(filePath, bundleHash) {
// find entry file with a name *.entry.js
const entryFiles = await findFiles(path$1.dirname(filePath), /.*\.entry\.js/);
if (!entryFiles.length) {
return;
}
const entryFileName = entryFiles[0];
const [entryFileContent, fileContent] = await Promise.all([
fs.readFile(entryFileName[1], "utf8"),
fs.readFile(filePath, "utf8"),
]);
const newFileContent = fileContent.replace(COMPONENT_TAG_TOKEN, `embeddable-component-${bundleHash}`);
const newEntryFileContent = entryFileContent.replace(COMPONENT_TAG_TOKEN.replaceAll("-", "_"), `embeddable_component_${bundleHash}`);
await Promise.all([
fs.writeFile(filePath, newFileContent),
fs.writeFile(entryFileName[1], newEntryFileContent),
]);
}
async function runStencil(ctx) {
var _a, _b, _c;
const logger = ((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.logger) || createNodeLogger();
const sys = ((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.sys) || createNodeSys({ process });
const devMode = !!((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch);
const isWindows = process.platform === "win32";
const validated = await loadConfig({
initTsConfig: true,
logger,
sys,
config: {
devMode,
maxConcurrentWorkers: isWindows ? 0 : 8, // workers break on windows
rootDir: ctx.client.webComponentRoot,
configPath: path$1.resolve(ctx.client.webComponentRoot, "stencil.config.ts"),
tsconfig: path$1.resolve(ctx.client.webComponentRoot, "tsconfig.json"),
namespace: "embeddable-wrapper",
srcDir: ctx.client.componentDir,
sourceMap: true, // always generate source maps in both dev and prod
minifyJs: !devMode,
minifyCss: !devMode,
outputTargets: [
{
type: "dist",
buildDir: path$1.resolve(ctx.client.buildDir, "dist"),
},
],
},
});
const compiler = await createCompiler(validated.config);
const buildResults = await compiler.build();
if (!devMode) {
if (buildResults.hasError) {
console.error("Stencil build error:", buildResults.diagnostics);
throw new Error("Stencil build error");
}
else {
await handleStencilBuildOutput(ctx);
}
await compiler.destroy();
}
process.chdir(ctx.client.rootDir);
}
async function handleStencilBuildOutput(ctx) {
var _a;
const entryFilePath = path$1.resolve(ctx.client.stencilBuild, "embeddable-wrapper.esm.js");
let fileName = "embeddable-wrapper.esm.js";
if (!((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch) && ctx.client.bundleHash) {
fileName = `embeddable-wrapper.esm-${ctx.client.bundleHash}.js`;
await addComponentTagName(entryFilePath, ctx.client.bundleHash);
}
await fs.rename(entryFilePath, path$1.resolve(ctx.client.stencilBuild, fileName));
}
async function generateSourceMap(ctx, pluginName) {
const componentBuildDir = path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName);
const stencilBuild = path$1.resolve(ctx.client.stencilBuild);
const tmpComponentDir = path$1.resolve(stencilBuild, ctx[pluginName].outputOptions.buildName);
await fs.cp(componentBuildDir, tmpComponentDir, { recursive: true });
const stencilFiles = await fs.readdir(stencilBuild);
const jsFiles = stencilFiles.filter((file) => file.toLowerCase().endsWith(".js"));
await Promise.all(jsFiles.map(async (jsFile) => {
try {
const chain = await sorcery.load(path$1.resolve(stencilBuild, jsFile));
// overwrite the existing file
await chain.write();
}
catch (e) {
// do nothing if a map file can not be generated
}
}));
await fs.rm(tmpComponentDir, { recursive: true });
}
const CREDENTIALS_DIR = path$1.resolve(os.homedir(), ".embeddable");
const CREDENTIALS_FILE = path$1.resolve(CREDENTIALS_DIR, "credentials");
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__default.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) => 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
*/
const getArgumentByKey = (key) => {
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;
};
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
*/
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
*/
const removeBuildSuccessFlag = async () => {
try {
await fs.unlink(SUCCESS_FLAG_FILE);
}
catch (_e) { }
};
/**
* Check if the build was successful
*/
const checkBuildSuccess = async () => {
try {
await fs.access(SUCCESS_FLAG_FILE);
return true;
}
catch (_e) {
return false;
}
};
const getPackageVersion = async (packageName) => {
const packageJsonPath = path__default.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, filePath) => {
try {
// Remove the file: prefix and resolve the path
const refPath = filePath.replace(/^file:/, "");
const absPath = path__default.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";
}
};
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(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({}));
// 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__default.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;
};
const hrtimeToISO8601 = (hrtime) => {
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`;
};
var cleanup = async (ctx) => {
await extractBuild(ctx);
await removeObsoleteDir(ctx.client.buildDir);
await moveBuildTOBuildDir(ctx);
};
async function createManifest({ ctx, typesFileName, metaFileName, editorsMetaFileName, stencilWrapperFileName, }) {
var _a, _b, _c, _d, _e;
const sdkVersions = await getSDKVersions();
// identify user's package manager and its version
let packageManager = "npm";
if ((_a = process.env.npm_config_user_agent) === null || _a === void 0 ? void 0 : _a.includes("yarn")) {
packageManager = "yarn";
}
if ((_b = process.env.npm_config_user_agent) === null || _b === void 0 ? void 0 : _b.includes("pnpm")) {
packageManager = "pnpm";
}
const packageManagerVersion = ((_d = (_c = process.env.npm_config_user_agent) === null || _c === void 0 ? void 0 : _c.match(/(\d+\.\d+\.\d+)/)) === null || _d === void 0 ? void 0 : _d[0]) ||
"unknown";
const globalHooks = await loadJson(path$1.resolve(ctx.client.buildDir, "globalHooks.json"));
// write manifest file with files with hash
const manifest = {
entryFiles: {
"embeddable-types.js": typesFileName,
"embeddable-components-meta.js": metaFileName,
"embeddable-editors-meta.js": editorsMetaFileName,
"embeddable-wrapper.esm.js": stencilWrapperFileName,
},
metadata: {
nodeVersion: process.version,
platform: process.platform,
bundleHash: ctx.client.bundleHash,
sdkVersions,
packageManager,
packageManagerVersion,
globalHooks,
metrics: {
buildTime: hrtimeToISO8601(ctx.buildTime),
},
origin: ((_e = ctx.dev) === null || _e === void 0 ? void 0 : _e.watch) ? "dev" : "push",
},
};
await fs.writeFile(path$1.join(ctx.client.tmpDir, "embeddable-manifest.json"), JSON.stringify(manifest));
}
async function extractBuild(ctx) {
// Ensure tmpDir is removed before attempting to rename a directory to it.
// This helps prevent EPERM errors on Windows if tmpDir already exists and is non-empty
// from a previous failed run or other reasons.
await fs.rm(ctx.client.tmpDir, { recursive: true, force: true });
const stencilBuildFiles = await findFiles(ctx.client.stencilBuild, /embeddable-wrapper.esm-[a-z0-9]+\.js/);
const [[, stencilWrapperFilePath]] = stencilBuildFiles || [];
const stencilWrapperFileName = path$1.basename(stencilWrapperFilePath);
await fs.rename(path$1.resolve(ctx.client.buildDir, ctx.client.stencilBuild), ctx.client.tmpDir);
const typesBuildFiles = await findFiles(ctx.client.buildDir, /embeddable-types-[a-z0-9]+\.js/);
const [[, typesFilePath]] = typesBuildFiles || [];
const typesFileName = path$1.basename(typesFilePath);
await fs.rename(typesFilePath, path$1.join(ctx.client.tmpDir, typesFileName));
const metaBuildFiles = await findFiles(ctx.client.buildDir, /embeddable-components-meta-[a-z0-9]+\.js/);
const [[, metaFilePath]] = metaBuildFiles || [];
const metaFileName = path$1.basename(metaFilePath);
await fs.rename(metaFilePath, path$1.join(ctx.client.tmpDir, metaFileName));
const editorsMetaBuildFiles = await findFiles(ctx.client.buildDir, /embeddable-editors-meta-[a-z0-9]+\.js/);
const [[, editorsMetaFilePath]] = editorsMetaBuildFiles || [];
const editorsMetaFileName = path$1.basename(editorsMetaFilePath);
await fs.rename(editorsMetaFilePath, path$1.join(ctx.client.tmpDir, editorsMetaFileName));
const themeFile = (await findFiles(ctx.client.buildDir, /embeddable-theme-[0-9a-f]+\.js/)) ||
[];
for (const [, themeFilePath] of themeFile) {
const themeFilePathFileName = path$1.basename(themeFilePath);
await fs.rename(themeFilePath, path$1.join(ctx.client.tmpDir, themeFilePathFileName));
}
const lifecycleFiles = (await findFiles(ctx.client.buildDir, /embeddable-lifecycle(?:-[0-9a-f]+)?\.js/)) || [];
for (const [, lifecycleFilePath] of lifecycleFiles) {
const lifecycleFilePathFileName = path$1.basename(lifecycleFilePath);
await fs.rename(lifecycleFilePath, path$1.join(ctx.client.tmpDir, lifecycleFilePathFileName));
}
await createManifest({
ctx,
typesFileName,
metaFileName,
editorsMetaFileName,
stencilWrapperFileName,
});
}
async function removeObsoleteDir(dir) {
await fs.rm(dir, { recursive: true });
}
async function moveBuildTOBuildDir(ctx) {
await fs.rename(ctx.client.tmpDir, ctx.client.buildDir);
}
var util$1;
(function (util) {
util.assertEqual = (val) => val;
function assertIs(_arg) { }
util.assertIs = assertIs;
function assertNever(_x) {
throw new Error();
}
util.assertNever = assertNever;
util.arrayToEnum = (items) => {
const obj = {};
for (const item of items) {
obj[item] = item;
}
return obj;
};
util.getValidEnumValues = (obj) => {
const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
const filtered = {};
for (const k of validKeys) {
filtered[k] = obj[k];
}
return util.objectValues(filtered);
};
util.objectValues = (obj) => {
return util.objectKeys(obj).map(function (e) {
return obj[e];
});
};
util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban
? (obj) => Object.keys(obj) // eslint-disable-line ban/ban
: (object) => {
const keys = [];
for (const key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
keys.push(key);
}
}
return keys;
};
util.find = (arr, checker) => {
for (const item of arr) {
if (checker(item))
return item;
}
return undefined;
};
util.isInteger = typeof Number.isInteger === "function"
? (val) => Number.isInteger(val) // eslint-disable-line ban/ban
: (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val;
function joinValues(array, separator = " | ") {
return array
.map((val) => (typeof val === "string" ? `'${val}'` : val))
.join(separator);
}
util.joinValues = joinValues;
util.jsonStringifyReplacer = (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
})(util$1 || (util$1 = {}));
var objectUtil;
(function (objectUtil) {
objectUtil.mergeShapes = (first, second) => {
return {
...first,
...second, // second overwrites first
};
};
})(objectUtil || (objectUtil = {}));
const ZodParsedType = util$1.arrayToEnum([
"string",
"nan",
"number",
"integer",
"float",
"boolean",
"date",
"bigint",
"symbol",
"function",
"undefined",
"null",
"array",
"object",
"unknown",
"promise",
"void",
"never",
"map",
"set",
]);
const getParsedType = (data) => {
const t = typeof data;
switch (t) {
case "undefined":
return ZodParsedType.undefined;
case "string":
return ZodParsedType.string;
case "number":
return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;
case "boolean":
return ZodParsedType.boolean;
case "function":
return ZodParsedType.function;
case "bigint":
return ZodParsedType.bigint;
case "symbol":
return ZodParsedType.symbol;
case "object":
if (Array.isArray(data)) {
return ZodParsedType.array;
}
if (data === null) {
return ZodParsedType.null;
}
if (data.then &&
typeof data.then === "function" &&
data.catch &&
typeof data.catch === "function") {
return ZodParsedType.promise;
}
if (typeof Map !== "undefined" && data instanceof Map) {
return ZodParsedType.map;
}
if (typeof Set !== "undefined" && data instanceof Set) {
return ZodParsedType.set;
}
if (typeof Date !== "undefined" && data instanceof Date) {
return ZodParsedType.date;
}
return ZodParsedType.object;
default:
return ZodParsedType.unknown;
}
};
const ZodIssueCode = util$1.arrayToEnum([
"invalid_type",
"invalid_literal",
"custom",
"invalid_union",
"invalid_union_discriminator",
"invalid_enum_value",
"unrecognized_keys",
"invalid_arguments",
"invalid_return_type",
"invalid_date",
"invalid_string",
"too_small",
"too_big",
"invalid_intersection_types",
"not_multiple_of",
"not_finite",
]);
const quotelessJson = (obj) => {
const json = JSON.stringify(obj, null, 2);
return json.replace(/"([^"]+)":/g, "$1:");
};
class ZodError extends Error {
get errors() {
return this.issues;
}
constructor(issues) {
super();
this.issues = [];
this.addIssue = (sub) => {
this.issues = [...this.issues, sub];
};
this.addIssues = (subs = []) => {
this.issues = [...this.issues, ...subs];
};
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) {
// eslint-disable-next-line ban/ban
Object.setPrototypeOf(this, actualProto);
}
else {
this.__proto__ = actualProto;
}
this.name = "ZodError";
this.issues = issues;
}
format(_mapper) {
const mapper = _mapper ||
function (issue) {
return issue.message;
};
const fieldErrors = { _errors: [] };
const processError = (error) => {
for (const issue of error.issues) {
if (issue.code === "invalid_union") {
issue.unionErrors.map(processError);
}
else if (issue.code === "invalid_return_type") {
processError(issue.returnTypeError);
}
else if (issue.code === "invalid_arguments") {
processError(issue.argumentsError);
}
else if (issue.path.length === 0) {
fieldErrors._errors.push(mapper(issue));
}
else {
let curr = fieldErrors;
let i = 0;
while (i < issue.path.length) {
const el = issue.path[i];
const terminal = i === issue.path.length - 1;
if (!terminal) {
curr[el] = curr[el] || { _errors: [] };
// if (typeof el === "string") {
// curr[el] = curr[el] || { _errors: [] };
// } else if (typeof el === "number") {
// const errorArray: any = [];
// errorArray._errors = [];
// curr[el] = curr[el] || errorArray;
// }
}
else {
curr[el] = curr[el] || { _errors: [] };
curr[el]._errors.push(mapper(issue));
}
curr = curr[el];
i++;
}
}
}
};
processError(this);
return fieldErrors;
}