@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/lib/buildGlobalHooks.d.ts +7 -0
- package/lib/buildPackage.d.ts +2 -0
- package/lib/defineConfig.d.ts +42 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.esm.js +465 -285
- package/lib/index.esm.js.map +1 -1
- package/lib/push.d.ts +1 -1
- package/loader/entryPoint.js +9 -1
- package/loader/entryPoint.test.js +1 -0
- package/package.json +4 -3
- package/src/build.test.ts +6 -0
- package/src/build.ts +2 -0
- package/src/buildGlobalHooks.int.test.ts +215 -0
- package/src/buildGlobalHooks.ts +252 -0
- package/src/buildGlobalHooks.unit.test.ts +273 -0
- package/src/buildPackage.test.ts +167 -0
- package/src/buildPackage.ts +53 -0
- package/src/buildTypes.test.ts +108 -6
- package/src/buildTypes.ts +41 -2
- package/src/cleanup.test.ts +8 -1
- package/src/cleanup.ts +32 -1
- package/src/defineConfig.test.ts +3 -0
- package/src/defineConfig.ts +29 -3
- package/src/dev.test.ts +13 -1
- package/src/dev.ts +37 -1
- package/src/generate.test.ts +2 -0
- package/src/generate.ts +24 -13
- package/src/index.ts +2 -1
- package/src/push.ts +17 -17
- package/templates/component.tsx.template +3 -1
- package/templates/embeddableThemeHook.js.template +28 -0
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
|
|
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
|
+
}
|
package/src/cleanup.test.ts
CHANGED
|
@@ -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,
|
package/src/defineConfig.test.ts
CHANGED
|
@@ -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": {},
|
package/src/defineConfig.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
290
|
+
wss?.clients?.forEach((ws) => {
|
|
255
291
|
ws.send(JSON.stringify({ type, ...meta }));
|
|
256
292
|
});
|
|
257
293
|
};
|
package/src/generate.test.ts
CHANGED
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|