UNPKG

@embeddable.com/sdk-core

Version:

Core Embeddable SDK module responsible for web-components bundling and publishing.

1,292 lines (1,273 loc) 748 kB
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; }