@embeddable.com/sdk-core 3.13.7-next.0 → 3.14.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/buildTypes.ts CHANGED
@@ -2,8 +2,14 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import * as vite from "vite";
4
4
  import ora from "ora";
5
- import { findFiles, getContentHash } from "@embeddable.com/sdk-utils";
6
5
  import { ResolvedEmbeddableConfig } from "./defineConfig";
6
+ import {
7
+ findFiles,
8
+ getComponentLibraryConfig,
9
+ getContentHash,
10
+ } from "@embeddable.com/sdk-utils";
11
+ import fg from "fast-glob";
12
+
7
13
  export const EMB_TYPE_FILE_REGEX = /^(.*)\.type\.emb\.[jt]s$/;
8
14
  export const EMB_OPTIONS_FILE_REGEX = /^(.*)\.options\.emb\.[jt]s$/;
9
15
 
@@ -26,7 +32,10 @@ async function generate(ctx: ResolvedEmbeddableConfig) {
26
32
  EMB_OPTIONS_FILE_REGEX,
27
33
  );
28
34
 
29
- const typeImports = typeFiles
35
+ const additionalImports =
36
+ await getAdditionalImportsFromInstalledLibraries(ctx);
37
+
38
+ const repositoryTypeImports = typeFiles
30
39
  .concat(optionsFiles)
31
40
  .map(
32
41
  ([_fileName, filePath]) =>
@@ -36,6 +45,8 @@ async function generate(ctx: ResolvedEmbeddableConfig) {
36
45
  )
37
46
  .join("\n");
38
47
 
48
+ const typeImports = additionalImports.join("\n") + repositoryTypeImports;
49
+
39
50
  await fs.writeFile(
40
51
  path.resolve(
41
52
  ctx.client.buildDir,
@@ -83,3 +94,31 @@ async function cleanup(ctx: ResolvedEmbeddableConfig) {
83
94
  path.resolve(ctx.client.buildDir, "embeddable-types-entry-point.js"),
84
95
  );
85
96
  }
97
+
98
+ async function getAdditionalImportsFromInstalledLibraries(
99
+ ctx: ResolvedEmbeddableConfig,
100
+ ) {
101
+ const componentLibraries = ctx.client.componentLibraries;
102
+ const additionalImports: string[] = [];
103
+ for (const componentLibrary of componentLibraries) {
104
+ const { libraryName } = getComponentLibraryConfig(componentLibrary);
105
+ try {
106
+ fg.sync(
107
+ path.resolve(
108
+ ctx.client.rootDir,
109
+ "node_modules",
110
+ libraryName,
111
+ "dist",
112
+ "embeddable-types-*.js",
113
+ ),
114
+ ).forEach((file: string) => {
115
+ const fileName = path.basename(file);
116
+ additionalImports.push(`import '${libraryName}/dist/${fileName}';`);
117
+ });
118
+ } catch (e) {
119
+ console.error(`Can't load component library: ${libraryName}`, e);
120
+ throw e;
121
+ }
122
+ }
123
+ return additionalImports;
124
+ }
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { createManifest } from "./cleanup";
4
- import { findFiles } from "@embeddable.com/sdk-utils";
4
+ import { findFiles, loadJson } from "@embeddable.com/sdk-utils";
5
5
  import { ResolvedEmbeddableConfig } from "./defineConfig";
6
6
  const ctx = {
7
7
  client: {
@@ -20,6 +20,7 @@ vi.mock("node:fs/promises", () => ({
20
20
 
21
21
  vi.mock("@embeddable.com/sdk-utils", () => ({
22
22
  findFiles: vi.fn(),
23
+ loadJson: vi.fn(),
23
24
  }));
24
25
 
25
26
  vi.mock("node:path", async () => {
@@ -38,6 +39,9 @@ describe("cleanup", () => {
38
39
  vi.mocked(fs.writeFile).mockImplementation(async () => undefined);
39
40
  vi.mocked(fs.rename).mockImplementation(async () => undefined);
40
41
  vi.mocked(findFiles).mockResolvedValue([["", ""]]);
42
+ vi.mocked(loadJson).mockResolvedValue({
43
+ test: "test_value",
44
+ });
41
45
  vi.mocked(path.basename).mockReturnValue("basename");
42
46
  vi.mocked(path.join).mockImplementation((...args) => args.join("/"));
43
47
 
@@ -79,6 +83,9 @@ describe("cleanup", () => {
79
83
  sdkVersions: {},
80
84
  packageManager: "npm",
81
85
  packageManagerVersion: "10.7.0",
86
+ globalHooks: {
87
+ test: "test_value",
88
+ },
82
89
  metrics: {
83
90
  buildTime: "PT1M20.001S",
84
91
  },
package/src/cleanup.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { findFiles } from "@embeddable.com/sdk-utils";
1
+ import { findFiles, loadJson } from "@embeddable.com/sdk-utils";
2
2
  import * as fs from "node:fs/promises";
3
3
  import * as path from "node:path";
4
4
  import { getSDKVersions, hrtimeToISO8601 } from "./utils";
@@ -41,6 +41,10 @@ export async function createManifest({
41
41
  process.env.npm_config_user_agent?.match(/(\d+\.\d+\.\d+)/)?.[0] ||
42
42
  "unknown";
43
43
 
44
+ const globalHooks = await loadJson(
45
+ path.resolve(ctx.client.buildDir, "globalHooks.json"),
46
+ );
47
+
44
48
  // write manifest file with files with hash
45
49
  const manifest = {
46
50
  entryFiles: {
@@ -56,6 +60,7 @@ export async function createManifest({
56
60
  sdkVersions,
57
61
  packageManager,
58
62
  packageManagerVersion,
63
+ globalHooks,
59
64
  metrics: {
60
65
  buildTime: hrtimeToISO8601(ctx.buildTime),
61
66
  },
@@ -114,6 +119,32 @@ async function extractBuild(ctx: ResolvedEmbeddableConfig) {
114
119
  path.join(ctx.client.tmpDir, editorsMetaFileName),
115
120
  );
116
121
 
122
+ const themeFile =
123
+ (await findFiles(ctx.client.buildDir, /embeddable-theme-[0-9a-f]+\.js/)) ||
124
+ [];
125
+
126
+ for (const [, themeFilePath] of themeFile) {
127
+ const themeFilePathFileName = path.basename(themeFilePath);
128
+ await fs.rename(
129
+ themeFilePath,
130
+ path.join(ctx.client.tmpDir, themeFilePathFileName),
131
+ );
132
+ }
133
+
134
+ const lifecycleFiles =
135
+ (await findFiles(
136
+ ctx.client.buildDir,
137
+ /embeddable-lifecycle(?:-[0-9a-f]+)?\.js/,
138
+ )) || [];
139
+
140
+ for (const [, lifecycleFilePath] of lifecycleFiles) {
141
+ const lifecycleFilePathFileName = path.basename(lifecycleFilePath);
142
+ await fs.rename(
143
+ lifecycleFilePath,
144
+ path.join(ctx.client.tmpDir, lifecycleFilePathFileName),
145
+ );
146
+ }
147
+
117
148
  await createManifest({
118
149
  ctx,
119
150
  typesFileName,
@@ -54,8 +54,11 @@ describe("defineConfig", () => {
54
54
  "buildDir": "/embeddable-sdk/packages/core-sdk",
55
55
  "bundleHash": undefined,
56
56
  "componentDir": "/embeddable-sdk/packages/core-sdk",
57
+ "componentLibraries": [],
58
+ "customizationFile": "/embeddable-sdk/packages/core-sdk",
57
59
  "errorFallbackComponent": "/embeddable-sdk/packages/core-sdk",
58
60
  "globalCss": "/embeddable-sdk/packages/core-sdk",
61
+ "lifecycleHooksFile": "/embeddable-sdk/packages/core-sdk",
59
62
  "modelsSrc": "/embeddable-sdk/packages/core-sdk",
60
63
  "presetsSrc": "/embeddable-sdk/packages/core-sdk",
61
64
  "rollupOptions": {},
@@ -2,7 +2,10 @@ import * as path from "node:path";
2
2
  import { existsSync } from "node:fs";
3
3
  import { RollupOptions } from "rollup";
4
4
  import { z } from "zod";
5
- import { errorFormatter } from "@embeddable.com/sdk-utils";
5
+ import {
6
+ errorFormatter,
7
+ ComponentLibraryConfig,
8
+ } from "@embeddable.com/sdk-utils";
6
9
 
7
10
  export type Region = "EU" | "US" | "legacy-US";
8
11
 
@@ -12,6 +15,7 @@ export type EmbeddableConfig = {
12
15
  build: (config: EmbeddableConfig) => Promise<unknown>;
13
16
  cleanup: (config: EmbeddableConfig) => Promise<unknown>;
14
17
  validate: (config: EmbeddableConfig) => Promise<unknown>;
18
+ buildPackage: (config: EmbeddableConfig) => Promise<unknown>;
15
19
  })[];
16
20
  pushModels?: boolean;
17
21
  pushComponents?: boolean;
@@ -34,6 +38,9 @@ export type EmbeddableConfig = {
34
38
  };
35
39
  rollupOptions?: RollupOptions;
36
40
  region?: Region;
41
+ componentLibraries?: string[] | ComponentLibraryConfig[];
42
+ customizationFile?: string;
43
+ lifecycleHooksFile?: string;
37
44
  };
38
45
 
39
46
  const PLUGIN_NAME = "sdk-react" as const;
@@ -58,6 +65,9 @@ export type ResolvedEmbeddableConfig = {
58
65
  archiveFile: string;
59
66
  errorFallbackComponent?: string;
60
67
  bundleHash?: string;
68
+ customizationFile: string;
69
+ lifecycleHooksFile: string;
70
+ componentLibraries: string[] | ComponentLibraryConfig[];
61
71
  viteConfig: {
62
72
  resolve?: {
63
73
  alias?: Record<string, string>;
@@ -79,7 +89,7 @@ export type ResolvedEmbeddableConfig = {
79
89
  rollbarAccessToken: string;
80
90
  plugins: EmbeddableConfig["plugins"];
81
91
  buildTime: [number, number];
82
- dev: {
92
+ dev?: {
83
93
  watch: boolean;
84
94
  logger: any;
85
95
  sys: any;
@@ -119,6 +129,12 @@ const REGION_CONFIGS = {
119
129
  },
120
130
  };
121
131
 
132
+ const ComponentLibraryConfigSchema = z.object({
133
+ name: z.string(),
134
+ include: z.array(z.string()).optional(),
135
+ exclude: z.array(z.string()).optional(),
136
+ });
137
+
122
138
  export const embeddableConfigSchema = z
123
139
  .object({
124
140
  plugins: z.array(z.function()),
@@ -139,6 +155,11 @@ export const embeddableConfigSchema = z
139
155
  presetsSrc: z.string().optional(),
140
156
  componentsSrc: z.string().optional(),
141
157
  globalCss: z.string().optional(),
158
+ customizationFile: z.string().optional(),
159
+ lifecycleHooksFile: z.string().optional(),
160
+ componentLibraries: z
161
+ .union([z.array(z.string()), z.array(ComponentLibraryConfigSchema)])
162
+ .optional(),
142
163
  viteConfig: z
143
164
  .object({
144
165
  resolve: z
@@ -185,12 +206,14 @@ export default (config: EmbeddableConfig) => {
185
206
  globalCss = "src/global.css",
186
207
  viteConfig = {},
187
208
  rollupOptions = {},
209
+ componentLibraries = [],
210
+ customizationFile = "embeddable.theme.ts",
211
+ lifecycleHooksFile = "lifecycle.config.ts",
188
212
  } = config;
189
213
 
190
214
  const regionConfig = REGION_CONFIGS[region];
191
215
 
192
216
  const __dirname = import.meta.dirname;
193
-
194
217
  const coreRoot = path.resolve(__dirname, "..");
195
218
  const clientRoot = process.cwd();
196
219
 
@@ -248,6 +271,9 @@ export default (config: EmbeddableConfig) => {
248
271
  bundleHash: undefined, // This will be set by the build process
249
272
  viteConfig,
250
273
  rollupOptions,
274
+ componentLibraries,
275
+ customizationFile: path.resolve(clientRoot, customizationFile),
276
+ lifecycleHooksFile: path.resolve(clientRoot, lifecycleHooksFile),
251
277
  },
252
278
  outputOptions: {
253
279
  typesEntryPointFilename: "embeddable-types-entry-point.js",
package/src/dev.test.ts CHANGED
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach, Mock } from "vitest";
2
2
  import * as http from "node:http";
3
3
  import axios from "axios";
4
4
  import provideConfig from "./provideConfig";
5
+ import buildGlobalHooks from "./buildGlobalHooks";
5
6
  import { getToken } from "./login";
6
7
  import * as chokidar from "chokidar";
7
8
  import dev from "./dev";
@@ -9,10 +10,12 @@ import { checkNodeVersion } from "./utils";
9
10
  import { createManifest } from "./cleanup";
10
11
  import prepare from "./prepare";
11
12
  import { WebSocketServer } from "ws";
12
- import { initLogger, logError } from "./logger";
13
+ import { logError } from "./logger";
13
14
  import { ResolvedEmbeddableConfig } from "./defineConfig";
15
+ import { RollupWatcher } from "rollup";
14
16
  // Mock dependencies
15
17
  vi.mock("./buildTypes", () => ({ default: vi.fn() }));
18
+ vi.mock("./buildGlobalHooks", () => ({ default: vi.fn() }));
16
19
  vi.mock("./prepare", () => ({ default: vi.fn(), removeIfExists: vi.fn() }));
17
20
  vi.mock("./generate", () => ({ default: vi.fn() }));
18
21
  vi.mock("./provideConfig", () => ({ default: vi.fn() }));
@@ -47,6 +50,7 @@ const mockConfig = {
47
50
  globalCss: "/mock/root/global.css",
48
51
  modelsSrc: "/mock/root/models",
49
52
  presetsSrc: "/mock/root/presets",
53
+ componentLibraries: [],
50
54
  },
51
55
  plugins: [],
52
56
  previewBaseUrl: "http://preview.example.com",
@@ -83,6 +87,13 @@ describe("dev command", () => {
83
87
  vi.mocked(axios.get).mockResolvedValue({
84
88
  data: [{ id: "mock-workspace" }],
85
89
  });
90
+
91
+ // @ts-ignore
92
+ const watcherMock: RollupWatcher = { on: vi.fn(), close: vi.fn() };
93
+ vi.mocked(buildGlobalHooks).mockResolvedValue({
94
+ themeWatcher: watcherMock,
95
+ lifecycleWatcher: watcherMock,
96
+ });
86
97
  });
87
98
 
88
99
  afterEach(() => {
@@ -104,6 +115,7 @@ describe("dev command", () => {
104
115
 
105
116
  // Call the listen callback to simulate the server being set up
106
117
  listenMock.mock.calls[0][1]();
118
+ expect(buildGlobalHooks).toHaveBeenCalled();
107
119
  expect(createManifest).toHaveBeenCalled();
108
120
 
109
121
  await expect.poll(() => chokidar.watch).toBeCalledTimes(2);
package/src/dev.ts CHANGED
@@ -35,6 +35,7 @@ import ora, { Ora } from "ora";
35
35
  import finalhandler from "finalhandler";
36
36
  import serveStatic from "serve-static";
37
37
  import { ResolvedEmbeddableConfig } from "./defineConfig";
38
+ import buildGlobalHooks from "./buildGlobalHooks";
38
39
  dotenv.config();
39
40
 
40
41
  let wss: WebSocketServer;
@@ -142,6 +143,15 @@ export default async () => {
142
143
 
143
144
  workspacePreparation.succeed("Workspace is ready");
144
145
 
146
+ const { themeWatcher, lifecycleWatcher } = await buildGlobalHooks(config);
147
+
148
+ if (themeWatcher) {
149
+ await globalHookWatcher(themeWatcher, config);
150
+ }
151
+ if (lifecycleWatcher) {
152
+ await globalHookWatcher(lifecycleWatcher, config);
153
+ }
154
+
145
155
  const server = http.createServer(
146
156
  async (request: IncomingMessage, res: ServerResponse) => {
147
157
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -219,6 +229,12 @@ export default async () => {
219
229
  const customGlobalCssWatch = globalCssWatcher(config);
220
230
  watchers.push(dataModelAndSecurityContextWatch);
221
231
  watchers.push(customGlobalCssWatch);
232
+ if (themeWatcher) {
233
+ watchers.push(themeWatcher);
234
+ }
235
+ if (lifecycleWatcher) {
236
+ watchers.push(lifecycleWatcher);
237
+ }
222
238
  });
223
239
  } catch (error: any) {
224
240
  await logError({ command: "dev", breadcrumbs, error });
@@ -250,8 +266,28 @@ const configureWatcher = async (
250
266
  });
251
267
  };
252
268
 
269
+ const globalHookWatcher = async (watcher: RollupWatcher, ctx: any) => {
270
+ watcher.on("change", (path) => {
271
+ changedFiles.push(path);
272
+ });
273
+
274
+ watcher.on("event", async (e) => {
275
+ if (e.code === "BUNDLE_START") {
276
+ sendMessage("componentsBuildStart", { changedFiles });
277
+ }
278
+ if (e.code === "BUNDLE_END") {
279
+ sendMessage("componentsBuildSuccess");
280
+ changedFiles = [];
281
+ }
282
+ if (e.code === "ERROR") {
283
+ sendMessage("componentsBuildError", { error: e.error?.message });
284
+ changedFiles = [];
285
+ }
286
+ });
287
+ };
288
+
253
289
  const sendMessage = (type: string, meta = {}) => {
254
- wss.clients.forEach((ws) => {
290
+ wss?.clients?.forEach((ws) => {
255
291
  ws.send(JSON.stringify({ type, ...meta }));
256
292
  });
257
293
  };
@@ -13,6 +13,8 @@ const config = {
13
13
  tmpDir: "tmpDir",
14
14
  stencilBuild: "stencilBuild",
15
15
  componentDir: "componentDir",
16
+ bundleHash: "hash",
17
+ componentLibraries: [],
16
18
  },
17
19
  core: {
18
20
  rootDir: "rootDir",
package/src/generate.ts CHANGED
@@ -2,8 +2,12 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { createNodeLogger, createNodeSys } from "@stencil/core/sys/node";
4
4
  import { createCompiler, loadConfig } from "@stencil/core/compiler";
5
- import { findFiles, getContentHash } from "@embeddable.com/sdk-utils";
6
5
  import { PluginName, ResolvedEmbeddableConfig } from "./defineConfig";
6
+ import {
7
+ findFiles,
8
+ getComponentLibraryConfig,
9
+ } from "@embeddable.com/sdk-utils";
10
+
7
11
  import * as sorcery from "sorcery";
8
12
 
9
13
  const STYLE_IMPORTS_TOKEN = "{{STYLES_IMPORT}}";
@@ -35,13 +39,27 @@ async function injectCSS(
35
39
  );
36
40
  const allFiles = await fs.readdir(CUSTOMER_BUILD);
37
41
 
38
- const cssFilesImportsStr = allFiles
42
+ const imports = allFiles
39
43
  .filter((fileName) => fileName.endsWith(".css"))
40
44
  .map(
41
45
  (fileName) =>
42
46
  `@import '../${ctx[pluginName].outputOptions.buildName}/${fileName}';`,
43
- )
44
- .join("\n");
47
+ );
48
+
49
+ const componentLibraries = ctx.client.componentLibraries;
50
+ for (const componentLibrary of componentLibraries) {
51
+ const { libraryName } = getComponentLibraryConfig(componentLibrary);
52
+ const allLibFiles = await fs.readdir(
53
+ path.resolve(ctx.client.rootDir, "node_modules", libraryName, "dist"),
54
+ );
55
+ allLibFiles
56
+ .filter((fileName) => fileName.endsWith(".css"))
57
+ .forEach((fileName) =>
58
+ imports.push(`@import '~${libraryName}/dist/${fileName}';`),
59
+ );
60
+ }
61
+
62
+ const cssFilesImportsStr = imports.join("\n");
45
63
 
46
64
  const content = await fs.readFile(
47
65
  path.resolve(ctx.core.templatesDir, "style.css.template"),
@@ -165,15 +183,8 @@ async function handleStencilBuildOutput(ctx: ResolvedEmbeddableConfig) {
165
183
 
166
184
  let fileName = "embeddable-wrapper.esm.js";
167
185
 
168
- if (!ctx.dev?.watch) {
169
- const entryFileContent = await fs.readFile(entryFilePath, "utf8");
170
-
171
- const fileHash = getContentHash(entryFileContent);
172
-
173
- fileName = `embeddable-wrapper.esm-${fileHash}.js`;
174
-
175
- ctx.client.bundleHash = fileHash;
176
-
186
+ if (!ctx.dev?.watch && ctx.client.bundleHash) {
187
+ fileName = `embeddable-wrapper.esm-${ctx.client.bundleHash}.js`;
177
188
  await addComponentTagName(entryFilePath, ctx.client.bundleHash);
178
189
  }
179
190
 
package/src/index.ts CHANGED
@@ -3,4 +3,5 @@ export { default as login } from "./login";
3
3
  export { default as push } from "./push";
4
4
  export { default as dev } from "./dev";
5
5
  export { default as defineConfig } from "./defineConfig";
6
- export type { ResolvedEmbeddableConfig } from "./defineConfig";
6
+ export { default as buildPackage } from "./buildPackage";
7
+ export type { ResolvedEmbeddableConfig } from "./defineConfig";
package/src/push.ts CHANGED
@@ -33,7 +33,7 @@ export default async () => {
33
33
  const isBuildSuccess = await checkBuildSuccess();
34
34
  if (!isBuildSuccess) {
35
35
  console.error(
36
- "Build failed or not completed. Please run `embeddable:build` first."
36
+ "Build failed or not completed. Please run `embeddable:build` first.",
37
37
  );
38
38
  process.exit(1);
39
39
  }
@@ -61,7 +61,7 @@ export default async () => {
61
61
  const { workspaceId, name: workspaceName } = await selectWorkspace(
62
62
  ora,
63
63
  config,
64
- token
64
+ token,
65
65
  );
66
66
 
67
67
  const workspacePreviewUrl = `${config.previewBaseUrl}/workspace/${workspaceId}`;
@@ -70,7 +70,7 @@ export default async () => {
70
70
  breadcrumbs.push("build archive");
71
71
  await buildArchive(config);
72
72
  spinnerPushing.info(
73
- `Publishing to ${workspaceName} using ${workspacePreviewUrl}...`
73
+ `Publishing to ${workspaceName} using ${workspacePreviewUrl}...`,
74
74
  );
75
75
 
76
76
  breadcrumbs.push("send build");
@@ -78,7 +78,7 @@ export default async () => {
78
78
 
79
79
  publishedSectionFeedback(config, spinnerPushing);
80
80
  spinnerPushing.succeed(
81
- `Published to ${workspaceName} using ${workspacePreviewUrl}`
81
+ `Published to ${workspaceName} using ${workspacePreviewUrl}`,
82
82
  );
83
83
  } catch (error: any) {
84
84
  spinnerPushing?.fail("Publishing failed");
@@ -91,7 +91,7 @@ export default async () => {
91
91
 
92
92
  const publishedSectionFeedback = (
93
93
  config: ResolvedEmbeddableConfig,
94
- spinnerPushing: Ora
94
+ spinnerPushing: Ora,
95
95
  ) => {
96
96
  config.pushModels && spinnerPushing.succeed("Models published");
97
97
  config.pushComponents && spinnerPushing.succeed("Components published");
@@ -109,7 +109,7 @@ async function pushByApiKey(config: ResolvedEmbeddableConfig, spinner: any) {
109
109
 
110
110
  if (!email || !/\S+@\S+\.\S+/.test(email)) {
111
111
  spinner.fail(
112
- "Invalid email provided. Please provide a valid email using --email (-e) flag"
112
+ "Invalid email provided. Please provide a valid email using --email (-e) flag",
113
113
  );
114
114
  process.exit(1);
115
115
  }
@@ -150,7 +150,7 @@ export async function buildArchive(config: ResolvedEmbeddableConfig) {
150
150
 
151
151
  if (!config.pushModels && !config.pushComponents) {
152
152
  spinnerArchive.fail(
153
- "Cannot push: both pushModels and pushComponents are disabled"
153
+ "Cannot push: both pushModels and pushComponents are disabled",
154
154
  );
155
155
  process.exit(1);
156
156
  }
@@ -160,11 +160,11 @@ export async function buildArchive(config: ResolvedEmbeddableConfig) {
160
160
  if (config.pushModels) {
161
161
  const cubeFilesList = await findFiles(
162
162
  config.client.modelsSrc || config.client.srcDir,
163
- CUBE_FILES
163
+ CUBE_FILES,
164
164
  );
165
165
  const contextFilesList = await findFiles(
166
166
  config.client.presetsSrc || config.client.srcDir,
167
- PRESET_FILES
167
+ PRESET_FILES,
168
168
  );
169
169
  filesList.push(
170
170
  ...cubeFilesList.map((entry): [string, string] => [
@@ -174,7 +174,7 @@ export async function buildArchive(config: ResolvedEmbeddableConfig) {
174
174
  ...contextFilesList.map((entry): [string, string] => [
175
175
  path.basename(entry[1]),
176
176
  entry[1],
177
- ])
177
+ ]),
178
178
  );
179
179
  }
180
180
 
@@ -214,14 +214,14 @@ export async function archive(args: {
214
214
 
215
215
  await archive.finalize();
216
216
 
217
- return new Promise((resolve, _reject) => {
218
- output.on("close", resolve);
217
+ return new Promise<void>((resolve: any, _reject) => {
218
+ output.on("close", () => resolve());
219
219
  });
220
220
  }
221
221
 
222
222
  export async function createFormData(
223
223
  filePath: string,
224
- metadata: Record<string, any>
224
+ metadata: Record<string, any>,
225
225
  ) {
226
226
  const { FormData, Blob } = await import("formdata-node");
227
227
  const { fileFromPath } = await import("formdata-node/file-from-path");
@@ -244,7 +244,7 @@ export async function sendBuildByApiKey(
244
244
  apiKey,
245
245
  email,
246
246
  message,
247
- }: { apiKey: string; email: string; message?: string }
247
+ }: { apiKey: string; email: string; message?: string },
248
248
  ) {
249
249
  const form = await createFormData(ctx.client.archiveFile, {
250
250
  pushModels: ctx.pushModels,
@@ -256,7 +256,7 @@ export async function sendBuildByApiKey(
256
256
  const response = await uploadFile(
257
257
  form,
258
258
  `${ctx.pushBaseUrl}/api/v1/bundle/upload`,
259
- apiKey
259
+ apiKey,
260
260
  );
261
261
  await fs.rm(ctx.client.archiveFile);
262
262
 
@@ -269,7 +269,7 @@ export async function sendBuild(
269
269
  workspaceId,
270
270
  token,
271
271
  message,
272
- }: { workspaceId: string; token: string; message?: string }
272
+ }: { workspaceId: string; token: string; message?: string },
273
273
  ) {
274
274
  const form = await createFormData(ctx.client.archiveFile, {
275
275
  pushModels: ctx.pushModels,
@@ -281,7 +281,7 @@ export async function sendBuild(
281
281
  await uploadFile(
282
282
  form,
283
283
  `${ctx.pushBaseUrl}/bundle/${workspaceId}/upload`,
284
- token
284
+ token,
285
285
  );
286
286
 
287
287
  await fs.rm(ctx.client.archiveFile);
@@ -14,6 +14,7 @@ export class EmbeddableComponent {
14
14
  @Prop() componentName: string;
15
15
  @Prop() props: string = '{}';
16
16
  @Prop() clientContext: string = '{}';
17
+ @Prop() theme: any = {};
17
18
 
18
19
  @Event({
19
20
  eventName: 'componentLoaded',
@@ -100,7 +101,8 @@ export class EmbeddableComponent {
100
101
  this.rootElement,
101
102
  this.componentName,
102
103
  JSON.parse(this.props),
103
- JSON.parse(this.clientContext)
104
+ JSON.parse(this.clientContext),
105
+ this.theme
104
106
  );
105
107
  }
106
108
  }
@@ -0,0 +1,28 @@
1
+ {{LIBRARY_THEME_IMPORTS}}
2
+ {{LOCAL_THEME_IMPORT}}
3
+
4
+ import {mergician} from 'mergician';
5
+
6
+ const parentProviders = [
7
+ {{ARRAY_OF_LIBRARY_THEME_PROVIDERS}}
8
+ ];
9
+
10
+ export default function combinedThemeProvider(clientContext) {
11
+ let parentTheme = {};
12
+
13
+ // 1. Sequentially call each library's theme, merging the result
14
+ for (const provider of parentProviders) {
15
+ if (typeof provider === 'function') {
16
+ const partial = provider(clientContext, parentTheme);
17
+
18
+ // Merge into parentTheme
19
+ parentTheme = mergician(parentTheme, partial);
20
+ }
21
+ }
22
+
23
+ if (typeof localThemeProvider === 'function') {
24
+ return localThemeProvider(clientContext, parentTheme);
25
+ }
26
+
27
+ return parentTheme;
28
+ }