@embeddable.com/sdk-core 3.13.6 → 3.14.0-next.1

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.
@@ -0,0 +1,273 @@
1
+ // buildGlobalHooks.unit.test.ts
2
+ import { describe, it, expect, beforeEach, vi, Mock } from "vitest";
3
+ import * as fs from "node:fs/promises";
4
+ import * as fsSync from "node:fs";
5
+ import * as vite from "vite";
6
+ import {
7
+ findFiles,
8
+ getContentHash,
9
+ getGlobalHooksMeta,
10
+ getComponentLibraryConfig,
11
+ } from "@embeddable.com/sdk-utils";
12
+
13
+ import buildGlobalHooks from "../src/buildGlobalHooks";
14
+ import { ResolvedEmbeddableConfig } from "./defineConfig";
15
+ import path from "node:path";
16
+
17
+ // Mock implementations
18
+ vi.mock("node:fs/promises");
19
+ vi.mock("node:fs");
20
+ vi.mock("vite");
21
+ vi.mock("@embeddable.com/sdk-utils", async () => {
22
+ const actual = await vi.importActual<
23
+ typeof import("@embeddable.com/sdk-utils")
24
+ >("@embeddable.com/sdk-utils");
25
+ return {
26
+ ...actual,
27
+ findFiles: vi.fn(),
28
+ getContentHash: vi.fn(),
29
+ getGlobalHooksMeta: vi.fn(),
30
+ getComponentLibraryConfig: vi.fn(),
31
+ };
32
+ });
33
+
34
+ const lifecyclePath = path.resolve(
35
+ process.cwd(),
36
+ "fake",
37
+ "root",
38
+ "embeddable.lifecycle.ts",
39
+ );
40
+ const themePath = path.resolve(
41
+ process.cwd(),
42
+ "fake",
43
+ "root",
44
+ "embeddable.theme.ts",
45
+ );
46
+
47
+ describe("buildGlobalHooks (Unit Tests)", () => {
48
+ beforeEach(() => {
49
+ vi.clearAllMocks();
50
+ });
51
+
52
+ it("should skip lifecycle building if file doesn't exist", async () => {
53
+ vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => {
54
+ // We want the code to see that /fake/root/embeddable.lifecycle.ts does NOT exist
55
+ if (p === lifecyclePath) {
56
+ return false;
57
+ }
58
+ // Otherwise, default to false
59
+ return false;
60
+ });
61
+
62
+ // Possibly not used, but let's mock anyway
63
+ (findFiles as Mock).mockResolvedValue([]);
64
+
65
+ // The aggregator template might be read if there's some library with a theme
66
+ (fs.readFile as Mock).mockResolvedValue("some template content");
67
+ (getContentHash as Mock).mockReturnValue("abc123");
68
+
69
+ const ctx: ResolvedEmbeddableConfig = {
70
+ client: {
71
+ srcDir: path.resolve(process.cwd(), "fake", "src"),
72
+ buildDir: path.resolve(process.cwd(), "fake", "build"),
73
+ rootDir: path.resolve(process.cwd(), "fake", "root"),
74
+ lifecycleHooksFile: lifecyclePath,
75
+ componentLibraries: [],
76
+ customizationFile: themePath,
77
+ },
78
+ core: {
79
+ templatesDir: "/fake/templates",
80
+ },
81
+ } as any;
82
+
83
+ await buildGlobalHooks(ctx);
84
+
85
+ // Because we skip building the repo lifecycle, no call to vite.build with that entry
86
+ expect(vite.build).not.toHaveBeenCalledWith(
87
+ expect.objectContaining({
88
+ build: expect.objectContaining({
89
+ lib: expect.objectContaining({
90
+ entry: lifecyclePath,
91
+ }),
92
+ }),
93
+ }),
94
+ );
95
+ });
96
+
97
+ it("should build lifecycle file if it exists", async () => {
98
+ // Now we say "repo lifecycle does exist"
99
+ vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => {
100
+ // If path is exactly the lifecycle file => true
101
+ return p === lifecyclePath;
102
+ });
103
+
104
+ // Just in case, but not strictly used here
105
+ (findFiles as Mock).mockResolvedValue([]);
106
+ // The aggregator template might or might not be read
107
+ (fs.readFile as Mock).mockResolvedValue("some file content");
108
+ (getContentHash as Mock).mockReturnValue("abc123");
109
+ (fs.rename as Mock).mockResolvedValue(undefined);
110
+ (vite.build as Mock).mockResolvedValue(undefined);
111
+
112
+ const ctx: ResolvedEmbeddableConfig = {
113
+ client: {
114
+ srcDir: path.resolve(process.cwd(), "fake", "src"),
115
+ buildDir: path.resolve(process.cwd(), "fake", "build"),
116
+ rootDir: path.resolve(process.cwd(), "fake", "root"),
117
+ lifecycleHooksFile: lifecyclePath,
118
+ customizationFile: themePath,
119
+ componentLibraries: [],
120
+ },
121
+ core: {
122
+ templatesDir: "/fake/templates",
123
+ },
124
+ } as any;
125
+
126
+ await buildGlobalHooks(ctx);
127
+
128
+ // We expect a call to build the lifecycle
129
+ expect(vite.build).toHaveBeenCalledWith(
130
+ expect.objectContaining({
131
+ build: expect.objectContaining({
132
+ lib: expect.objectContaining({
133
+ entry: lifecyclePath,
134
+ fileName: "embeddable-lifecycle",
135
+ }),
136
+ }),
137
+ }),
138
+ );
139
+ });
140
+
141
+ it("should build theme aggregator if libraries exist with themeProvider references", async () => {
142
+ // aggregator template read is guaranteed
143
+ (fs.readFile as Mock).mockResolvedValue("template content");
144
+ (getContentHash as Mock).mockReturnValue("xyz777");
145
+ (fs.rename as Mock).mockResolvedValue(undefined);
146
+ (vite.build as Mock).mockResolvedValue(undefined);
147
+
148
+ // Suppose we skip the lifecycle, but aggregator is still built
149
+ vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => {
150
+ // lifecycle => false
151
+ if (p === lifecyclePath) return false;
152
+ // local theme => true
153
+ if (p === themePath) return true;
154
+ return false;
155
+ });
156
+
157
+ (getComponentLibraryConfig as Mock).mockImplementation((cfg: any) => ({
158
+ libraryName: cfg.name,
159
+ }));
160
+
161
+ // Each library call to getGlobalHooksMeta occurs twice:
162
+ // 1) buildThemeHook -> aggregator
163
+ // 2) buildLifecycleHooks -> but we skip it if lifecycle doesn't exist
164
+ // The code still calls getGlobalHooksMeta for each library in buildLifecycleHooks
165
+ // So we must provide 4 total mock results for 2 libraries => aggregator + lifecycle each.
166
+
167
+ (getGlobalHooksMeta as Mock)
168
+ // aggregator call #1: libA
169
+ .mockResolvedValueOnce({
170
+ themeProvider: "libA-theme.js",
171
+ lifecycleHooks: [],
172
+ })
173
+ // aggregator call #2: libB
174
+ .mockResolvedValueOnce({
175
+ themeProvider: "libB-theme.js",
176
+ lifecycleHooks: [],
177
+ })
178
+ // lifecycle call #1: libA
179
+ .mockResolvedValueOnce({
180
+ themeProvider: "libA-theme.js",
181
+ lifecycleHooks: [],
182
+ })
183
+ // lifecycle call #2: libB
184
+ .mockResolvedValueOnce({
185
+ themeProvider: "libB-theme.js",
186
+ lifecycleHooks: [],
187
+ });
188
+
189
+ const ctx: ResolvedEmbeddableConfig = {
190
+ client: {
191
+ srcDir: path.resolve(process.cwd(), "fake", "src"),
192
+ buildDir: path.resolve(process.cwd(), "fake", "build"),
193
+ rootDir: path.resolve(process.cwd(), "fake", "root"),
194
+ lifecycleHooksFile: lifecyclePath,
195
+ customizationFile: themePath,
196
+ componentLibraries: [{ name: "libA" }, { name: "libB" }],
197
+ },
198
+ core: {
199
+ templatesDir: "/fake/templates",
200
+ },
201
+ } as any;
202
+
203
+ await buildGlobalHooks(ctx);
204
+
205
+ // aggregator => build with entry = /fake/build/embeddableThemeHook.js
206
+ expect(vite.build).toHaveBeenCalledWith(
207
+ expect.objectContaining({
208
+ build: expect.objectContaining({
209
+ lib: expect.objectContaining({
210
+ entry: expect.stringContaining("embeddableThemeHook.js"),
211
+ fileName: `embeddable-theme-xyz777`, // from getContentHash
212
+ }),
213
+ }),
214
+ }),
215
+ );
216
+ });
217
+
218
+ it("should skip aggregator if no library has themeProvider and no local theme", async () => {
219
+ (fs.readFile as Mock).mockResolvedValue("template content");
220
+ (getContentHash as Mock).mockReturnValue("someHash");
221
+ (fs.rename as Mock).mockResolvedValue(undefined);
222
+ (vite.build as Mock).mockResolvedValue(undefined);
223
+
224
+ vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => {
225
+ // Suppose no local theme => false
226
+ if (p === themePath) return false;
227
+ if (p === lifecyclePath) return false;
228
+ return false;
229
+ });
230
+
231
+ // Each library is missing a themeProvider
232
+ (getComponentLibraryConfig as Mock).mockImplementation((cfg: any) => ({
233
+ libraryName: cfg.name,
234
+ }));
235
+ // no theme, so aggregator is skipped
236
+ (getGlobalHooksMeta as Mock).mockResolvedValue({
237
+ themeProvider: null,
238
+ lifecycleHooks: [],
239
+ });
240
+
241
+ const ctx: ResolvedEmbeddableConfig = {
242
+ client: {
243
+ srcDir: path.resolve(process.cwd(), "fake", "src"),
244
+ buildDir: path.resolve(process.cwd(), "fake", "build"),
245
+ rootDir: path.resolve(process.cwd(), "fake", "root"),
246
+ lifecycleHooksFile: lifecyclePath,
247
+ customizationFile: themePath,
248
+ componentLibraries: [
249
+ { name: "libA" }, // no theme
250
+ ],
251
+ },
252
+ core: {
253
+ templatesDir: "/fake/templates",
254
+ },
255
+ } as any;
256
+
257
+ await buildGlobalHooks(ctx);
258
+
259
+ // aggregator not built
260
+ // We do an exact check: "not toHaveBeenCalledWith"
261
+ // But the code might still do a build for the lifecycle if it existed.
262
+ // We said the lifecycle is false => so no build for aggregator
263
+ expect(vite.build).not.toHaveBeenCalledWith(
264
+ expect.objectContaining({
265
+ build: expect.objectContaining({
266
+ lib: expect.objectContaining({
267
+ entry: expect.stringContaining("embeddableThemeHook.js"),
268
+ }),
269
+ }),
270
+ }),
271
+ );
272
+ });
273
+ });
@@ -0,0 +1,167 @@
1
+ import path from "node:path";
2
+ import { describe, it, expect, vi, beforeEach } from "vitest";
3
+ import * as fs from "node:fs/promises";
4
+ import * as fsSync from "node:fs";
5
+ import process from "node:process";
6
+
7
+ import buildPackage from "./buildPackage";
8
+ import buildTypes from "./buildTypes";
9
+ import buildGlobalHooks from "./buildGlobalHooks";
10
+ import provideConfig from "./provideConfig";
11
+ import {
12
+ checkNodeVersion,
13
+ removeBuildSuccessFlag,
14
+ storeBuildSuccessFlag,
15
+ } from "./utils";
16
+ import { initLogger, logError } from "./logger";
17
+ import { ResolvedEmbeddableConfig } from "./defineConfig";
18
+
19
+ // ----------------- MOCKS ----------------- //
20
+ vi.mock("node:fs/promises");
21
+ vi.mock("node:fs");
22
+
23
+ // Mock buildTypes
24
+ vi.mock("./buildTypes", () => ({
25
+ default: vi.fn(),
26
+ }));
27
+
28
+ // Mock buildGlobalHooks
29
+ vi.mock("./buildGlobalHooks", () => ({
30
+ default: vi.fn(),
31
+ }));
32
+
33
+ // Mock provideConfig
34
+ vi.mock("./provideConfig", () => ({
35
+ default: vi.fn(),
36
+ }));
37
+
38
+ // Mock utils
39
+ vi.mock("./utils", async () => {
40
+ const actualUtils =
41
+ await vi.importActual<typeof import("./utils")>("./utils");
42
+ return {
43
+ ...actualUtils,
44
+ checkNodeVersion: vi.fn(),
45
+ removeBuildSuccessFlag: vi.fn(),
46
+ storeBuildSuccessFlag: vi.fn(),
47
+ };
48
+ });
49
+
50
+ // Mock logger
51
+ vi.mock("./logger", () => ({
52
+ initLogger: vi.fn().mockResolvedValue(undefined),
53
+ logError: vi.fn().mockResolvedValue(undefined),
54
+ }));
55
+
56
+ // ----------------- TESTS ----------------- //
57
+ describe("buildPackage", () => {
58
+ // We'll create a sample plugin for testing
59
+ const mockPlugin = {
60
+ pluginName: "testPlugin",
61
+ validate: vi.fn(),
62
+ buildPackage: vi.fn(),
63
+ };
64
+
65
+ const rootDir = path.resolve("fake", "root");
66
+ const buildDir = path.resolve("fake", "build");
67
+ const distDir = path.resolve(rootDir, "dist");
68
+
69
+ // We'll also create a sample config
70
+ const config = {
71
+ client: {
72
+ rootDir,
73
+ buildDir,
74
+ },
75
+ plugins: [() => mockPlugin],
76
+ } as unknown as ResolvedEmbeddableConfig;
77
+
78
+ beforeEach(() => {
79
+ // Clear mocks before each test to avoid cross-test interference
80
+ vi.clearAllMocks();
81
+
82
+ // By default, provideConfig will return our sample config
83
+ vi.mocked(provideConfig).mockResolvedValue(config);
84
+
85
+ // Let's ensure fsSync.existsSync returns false by default
86
+ // (so that our prepare() logic tries to mkdir, etc.)
87
+ vi.spyOn(fsSync, "existsSync").mockReturnValue(false);
88
+ });
89
+
90
+ it("should call all main steps in a successful scenario", async () => {
91
+ await buildPackage();
92
+
93
+ // 1. Logger
94
+ expect(initLogger).toHaveBeenCalledWith("package");
95
+
96
+ // 2. checkNodeVersion and removeBuildSuccessFlag
97
+ expect(checkNodeVersion).toHaveBeenCalled();
98
+ expect(removeBuildSuccessFlag).toHaveBeenCalled();
99
+
100
+ // 3. provideConfig -> must be called, returns our config
101
+ expect(provideConfig).toHaveBeenCalled();
102
+
103
+ // 4. prepare() logic -> it checks if buildDir exists, removes if so, then mkdir
104
+ // Because we do cross-platform checks, we confirm the path used is distDir
105
+ expect(fsSync.existsSync).toHaveBeenCalledWith(distDir);
106
+ // Because it returned false, we do not remove
107
+ expect(fs.rm).toHaveBeenCalledTimes(0);
108
+ // Instead we create it
109
+ expect(fs.mkdir).toHaveBeenCalledWith(distDir);
110
+
111
+ // 5. buildTypes & buildGlobalHooks
112
+ expect(buildTypes).toHaveBeenCalledWith(config);
113
+ expect(buildGlobalHooks).toHaveBeenCalledWith(config);
114
+
115
+ // 6. Plugin calls
116
+ expect(mockPlugin.validate).toHaveBeenCalledWith(config);
117
+ expect(mockPlugin.buildPackage).toHaveBeenCalledWith(config);
118
+
119
+ // 7. storeBuildSuccessFlag
120
+ expect(storeBuildSuccessFlag).toHaveBeenCalled();
121
+ });
122
+
123
+ it("should remove and recreate buildDir if it already exists", async () => {
124
+ // This time let's simulate that buildDir already exists
125
+ vi.mocked(fsSync.existsSync).mockReturnValue(true);
126
+
127
+ await buildPackage();
128
+
129
+ // Because buildDir exists, prepare() should remove it
130
+ expect(fs.rm).toHaveBeenCalledWith(distDir, { recursive: true });
131
+ // Then re-create it
132
+ expect(fs.mkdir).toHaveBeenCalledWith(distDir);
133
+ });
134
+
135
+ it("should log an error, call logError, and exit(1) if an error occurs", async () => {
136
+ // We'll simulate an error in removeBuildSuccessFlag
137
+ const error = new Error("test error");
138
+ vi.mocked(removeBuildSuccessFlag).mockRejectedValueOnce(error);
139
+
140
+ // We want to check process.exit(1) is called
141
+ // but calling exit actually kills the process, so we mock it
142
+ const originalConsoleLog = console.log;
143
+ console.log = vi.fn();
144
+ const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => {
145
+ throw new Error("process.exit called");
146
+ });
147
+
148
+ // We expect buildPackage to throw because exit is called
149
+ await expect(buildPackage()).rejects.toThrow("process.exit called");
150
+
151
+ // We also expect that logError has been called with the correct params
152
+ expect(logError).toHaveBeenCalledWith({
153
+ command: "package",
154
+ breadcrumbs: ["checkNodeVersion"], // because it fails after calling checkNodeVersion
155
+ error,
156
+ });
157
+
158
+ // And the error is printed
159
+ expect(console.log).toHaveBeenCalledWith(error);
160
+
161
+ // Finally, we confirm process.exit(1) was triggered
162
+ expect(process.exit).toHaveBeenCalledWith(1);
163
+
164
+ console.log = originalConsoleLog;
165
+ exitSpy.mockRestore();
166
+ });
167
+ });
@@ -0,0 +1,53 @@
1
+ import buildTypes from "./buildTypes";
2
+ import buildGlobalHooks from "./buildGlobalHooks";
3
+ import provideConfig from "./provideConfig";
4
+ import {
5
+ checkNodeVersion,
6
+ removeBuildSuccessFlag,
7
+ storeBuildSuccessFlag,
8
+ } from "./utils";
9
+ import { initLogger, logError } from "./logger";
10
+ import * as path from "node:path";
11
+ import * as fs from "node:fs/promises";
12
+ import * as fsSync from "node:fs";
13
+
14
+ export default async () => {
15
+ await initLogger("package");
16
+ const breadcrumbs: string[] = [];
17
+
18
+ try {
19
+ const startTime = process.hrtime();
20
+ await checkNodeVersion();
21
+ breadcrumbs.push("checkNodeVersion");
22
+ await removeBuildSuccessFlag();
23
+
24
+ const config = await provideConfig();
25
+ config.client.buildDir = path.resolve(config.client.rootDir, "dist");
26
+ await prepare(config);
27
+ await buildTypes(config);
28
+ await buildGlobalHooks(config);
29
+
30
+ for (const getPlugin of config.plugins) {
31
+ const plugin = getPlugin();
32
+
33
+ breadcrumbs.push(`${plugin.pluginName}: validate`);
34
+ await plugin.validate(config);
35
+ breadcrumbs.push(`${plugin.pluginName}: buildPackage`);
36
+ await plugin.buildPackage(config);
37
+ }
38
+
39
+ // Calculating build time in seconds
40
+ config.buildTime = process.hrtime(startTime);
41
+ await storeBuildSuccessFlag();
42
+ } catch (error: any) {
43
+ await logError({ command: "package", breadcrumbs, error });
44
+ console.log(error);
45
+ process.exit(1);
46
+ }
47
+ };
48
+
49
+ const prepare = async (config: any) => {
50
+ if (fsSync.existsSync(config.client.buildDir))
51
+ await fs.rm(config.client.buildDir, { recursive: true });
52
+ await fs.mkdir(config.client.buildDir);
53
+ };
@@ -1,14 +1,24 @@
1
1
  import * as fs from "node:fs/promises";
2
- import buildTypes, { EMB_TYPE_FILE_REGEX } from "./buildTypes";
3
2
  import * as path from "node:path";
3
+ import { describe, it, expect, vi, beforeEach } from "vitest";
4
+ import buildTypes, { EMB_TYPE_FILE_REGEX } from "./buildTypes";
4
5
  import { build } from "vite";
5
- import { findFiles, getContentHash } from "@embeddable.com/sdk-utils";
6
6
  import { ResolvedEmbeddableConfig } from "./defineConfig";
7
+ import {
8
+ findFiles,
9
+ getContentHash,
10
+ getComponentLibraryConfig,
11
+ } from "@embeddable.com/sdk-utils";
12
+
13
+ import fg from "fast-glob";
14
+
15
+ // The same config you already have
7
16
  const config = {
8
17
  client: {
9
18
  srcDir: "src",
10
19
  rootDir: "root",
11
20
  buildDir: "build",
21
+ componentLibraries: [],
12
22
  },
13
23
  outputOptions: {
14
24
  typesEntryPointFilename: "typesEntryPointFilename",
@@ -31,12 +41,20 @@ vi.mock("ora", () => ({
31
41
  vi.mock("@embeddable.com/sdk-utils", () => ({
32
42
  findFiles: vi.fn(),
33
43
  getContentHash: vi.fn(),
44
+ getComponentLibraryConfig: vi.fn(),
34
45
  }));
35
46
 
36
- vi.mock("node:path", () => ({
37
- resolve: vi.fn(),
38
- relative: vi.fn(),
39
- }));
47
+ vi.mock("node:path", async () => {
48
+ const actualPath =
49
+ await vi.importActual<typeof import("node:path")>("node:path");
50
+ return {
51
+ ...actualPath,
52
+ resolve: vi.fn(),
53
+ relative: vi.fn(),
54
+ join: vi.fn(),
55
+ dirname: vi.fn(),
56
+ };
57
+ });
40
58
 
41
59
  vi.mock("node:fs/promises", () => ({
42
60
  writeFile: vi.fn(),
@@ -49,12 +67,30 @@ vi.mock("vite", () => ({
49
67
  build: vi.fn(),
50
68
  }));
51
69
 
70
+ vi.mock("fast-glob", () => ({
71
+ default: {
72
+ sync: vi.fn(),
73
+ },
74
+ }));
75
+
52
76
  describe("buildTypes", () => {
53
77
  beforeEach(() => {
78
+ vi.clearAllMocks();
79
+
54
80
  vi.mocked(findFiles).mockResolvedValue([["fileName", "filePath"]]);
55
81
  vi.mocked(path.relative).mockReturnValue("relativePath");
56
82
  vi.mocked(path.resolve).mockReturnValue("resolvedPath");
57
83
  vi.mocked(fs.readFile).mockResolvedValue("fileContent");
84
+ vi.mocked(getContentHash).mockReturnValue("somehash");
85
+ // By default, no libraries => no fast-glob usage
86
+ vi.mocked(fg.sync).mockReturnValue([]);
87
+
88
+ // We also default to returning an empty config from getComponentLibraryConfig
89
+ vi.mocked(getComponentLibraryConfig).mockReturnValue({
90
+ libraryName: "",
91
+ include: [],
92
+ exclude: [],
93
+ });
58
94
  });
59
95
 
60
96
  it("should build types", async () => {
@@ -98,4 +134,70 @@ import '../relativePath';`,
98
134
  expect(getContentHash).not.toHaveBeenCalled();
99
135
  expect(fs.rename).not.toHaveBeenCalled();
100
136
  });
137
+ it("should import types from installed libraries if present", async () => {
138
+ const configWithLibrary = {
139
+ ...config,
140
+ client: {
141
+ ...config.client,
142
+ componentLibraries: [
143
+ {
144
+ /* any library config object */
145
+ },
146
+ ],
147
+ },
148
+ } as unknown as ResolvedEmbeddableConfig;
149
+
150
+ // Mock getComponentLibraryConfig => returns libraryName: "my-lib"
151
+ vi.mocked(getComponentLibraryConfig).mockReturnValue({
152
+ libraryName: "my-lib",
153
+ include: [],
154
+ exclude: [],
155
+ });
156
+
157
+ vi.mocked(fg.sync).mockReturnValue([
158
+ "/fake/path/node_modules/my-lib/dist/embeddable-types-abc123.js",
159
+ "/fake/path/node_modules/my-lib/dist/embeddable-types-xyz987.js",
160
+ ]);
161
+
162
+ // Now run
163
+ await buildTypes(configWithLibrary);
164
+
165
+ const written = vi.mocked(fs.writeFile).mock.calls[0][1];
166
+
167
+ expect(written).toContain(
168
+ `import 'my-lib/dist/embeddable-types-abc123.js';`,
169
+ );
170
+ expect(written).toContain(
171
+ `import 'my-lib/dist/embeddable-types-xyz987.js';`,
172
+ );
173
+ expect(written).toContain(`import '../relativePath';`);
174
+ });
175
+
176
+ it("should throw if an error occurs when loading a library", async () => {
177
+ const configWithLibrary = {
178
+ ...config,
179
+ client: {
180
+ ...config.client,
181
+ componentLibraries: [
182
+ {
183
+ /* any library config object */
184
+ },
185
+ ],
186
+ },
187
+ } as unknown as ResolvedEmbeddableConfig;
188
+
189
+ vi.mocked(getComponentLibraryConfig).mockReturnValue({
190
+ libraryName: "my-broken-lib",
191
+ include: [],
192
+ exclude: [],
193
+ });
194
+
195
+ vi.mocked(fg.sync).mockImplementation(() => {
196
+ throw new Error("some library error");
197
+ });
198
+
199
+ await expect(buildTypes(configWithLibrary)).rejects.toThrow(
200
+ "some library error",
201
+ );
202
+ });
101
203
  });