@embeddable.com/sdk-core 4.4.0-next.0 → 4.4.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.
- package/lib/defineConfig.d.ts +4 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.esm.js +10 -4
- package/lib/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/defineConfig.ts +5 -0
- package/src/dev.test.ts +28 -0
- package/src/dev.ts +1 -0
- package/src/generate.test.ts +65 -3
- package/src/generate.ts +5 -2
- package/src/index.ts +1 -1
- package/src/push.test.ts +49 -2
- package/src/push.ts +2 -0
- package/templates/component.tsx.template +13 -4
package/package.json
CHANGED
package/src/defineConfig.ts
CHANGED
|
@@ -109,9 +109,14 @@ export type ResolvedEmbeddableConfig = {
|
|
|
109
109
|
buildName: string;
|
|
110
110
|
componentsEntryPointFilename: string;
|
|
111
111
|
};
|
|
112
|
+
pluginFlags?: PluginFlags;
|
|
112
113
|
};
|
|
113
114
|
};
|
|
114
115
|
|
|
116
|
+
export type PluginFlags = {
|
|
117
|
+
supportsOnComponentReadyHook: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
115
120
|
const REGION_CONFIGS = {
|
|
116
121
|
EU: {
|
|
117
122
|
pushBaseUrl: "https://api.eu.embeddable.com",
|
package/src/dev.test.ts
CHANGED
|
@@ -1103,6 +1103,34 @@ describe("dev command", () => {
|
|
|
1103
1103
|
|
|
1104
1104
|
process.argv = originalArgv;
|
|
1105
1105
|
});
|
|
1106
|
+
|
|
1107
|
+
it("should include pushEmbeddables in the dev-workspace request body when true", async () => {
|
|
1108
|
+
vi.mocked(provideConfig).mockResolvedValue({
|
|
1109
|
+
...mockConfig,
|
|
1110
|
+
pushEmbeddables: true,
|
|
1111
|
+
} as unknown as ResolvedEmbeddableConfig);
|
|
1112
|
+
|
|
1113
|
+
await dev();
|
|
1114
|
+
|
|
1115
|
+
const devWorkspaceCall = vi.mocked(axios.post).mock.calls.find(
|
|
1116
|
+
(call) => String(call[0]).includes("dev-workspace"),
|
|
1117
|
+
);
|
|
1118
|
+
expect(devWorkspaceCall?.[1]).toMatchObject({ pushEmbeddables: true });
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
it("should include pushEmbeddables in the dev-workspace request body when false", async () => {
|
|
1122
|
+
vi.mocked(provideConfig).mockResolvedValue({
|
|
1123
|
+
...mockConfig,
|
|
1124
|
+
pushEmbeddables: false,
|
|
1125
|
+
} as unknown as ResolvedEmbeddableConfig);
|
|
1126
|
+
|
|
1127
|
+
await dev();
|
|
1128
|
+
|
|
1129
|
+
const devWorkspaceCall = vi.mocked(axios.post).mock.calls.find(
|
|
1130
|
+
(call) => String(call[0]).includes("dev-workspace"),
|
|
1131
|
+
);
|
|
1132
|
+
expect(devWorkspaceCall?.[1]).toMatchObject({ pushEmbeddables: false });
|
|
1133
|
+
});
|
|
1106
1134
|
});
|
|
1107
1135
|
|
|
1108
1136
|
describe("addToGitignore", () => {
|
package/src/dev.ts
CHANGED
package/src/generate.test.ts
CHANGED
|
@@ -254,9 +254,9 @@ describe("generateDTS", () => {
|
|
|
254
254
|
"embeddable-wrapper.esm.js",
|
|
255
255
|
] as any);
|
|
256
256
|
vi.mocked(path.resolve).mockImplementation((...args) => args.join("/"));
|
|
257
|
-
// Template contains
|
|
257
|
+
// Template contains all tokens so we can verify replacement
|
|
258
258
|
vi.mocked(fs.readFile).mockResolvedValue(
|
|
259
|
-
"replace-this-with-component-name {{RENDER_IMPORT}}",
|
|
259
|
+
"replace-this-with-component-name {{RENDER_IMPORT}} {{PLUGIN_FLAGS}}",
|
|
260
260
|
);
|
|
261
261
|
vi.mocked(loadConfig).mockResolvedValue({ config: {} } as any);
|
|
262
262
|
vi.mocked(createCompiler).mockResolvedValue({
|
|
@@ -290,6 +290,19 @@ describe("generateDTS", () => {
|
|
|
290
290
|
);
|
|
291
291
|
});
|
|
292
292
|
|
|
293
|
+
it("should replace {{PLUGIN_FLAGS}} token with empty pluginFlags", async () => {
|
|
294
|
+
await generateDTS(config as unknown as ResolvedEmbeddableConfig);
|
|
295
|
+
|
|
296
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
297
|
+
"componentDir/component.tsx",
|
|
298
|
+
expect.stringContaining("const pluginFlags: Partial<PluginFlags> = {}"),
|
|
299
|
+
);
|
|
300
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
301
|
+
"componentDir/component.tsx",
|
|
302
|
+
expect.not.stringContaining("{{PLUGIN_FLAGS}}"),
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
293
306
|
it("should call loadConfig with devMode=false and sourceMap=false", async () => {
|
|
294
307
|
await generateDTS(config as unknown as ResolvedEmbeddableConfig);
|
|
295
308
|
|
|
@@ -333,7 +346,7 @@ describe("injectBundleRender cross-platform paths", () => {
|
|
|
333
346
|
|
|
334
347
|
beforeEach(() => {
|
|
335
348
|
vi.mocked(path.resolve).mockImplementation((...args) => args.join("/"));
|
|
336
|
-
vi.mocked(fs.readFile).mockResolvedValue("{{RENDER_IMPORT}}");
|
|
349
|
+
vi.mocked(fs.readFile).mockResolvedValue("{{RENDER_IMPORT}}\n{{PLUGIN_FLAGS}}");
|
|
337
350
|
});
|
|
338
351
|
|
|
339
352
|
it("should use forward slashes in import when path.relative returns unix path", async () => {
|
|
@@ -363,6 +376,55 @@ describe("injectBundleRender cross-platform paths", () => {
|
|
|
363
376
|
expect.stringContaining("import render from '../../buildDir/buildName/render.js'"),
|
|
364
377
|
);
|
|
365
378
|
});
|
|
379
|
+
|
|
380
|
+
it("should inject pluginFlags from config into component.tsx", async () => {
|
|
381
|
+
vi.mocked(path.relative).mockReturnValue("../../buildDir/buildName");
|
|
382
|
+
const ctxWithPluginFlags = {
|
|
383
|
+
...ctxWithFileName,
|
|
384
|
+
"sdk-react": {
|
|
385
|
+
...ctxWithFileName["sdk-react"],
|
|
386
|
+
pluginFlags: { supportsOnComponentReadyHook: true },
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
await injectBundleRender(
|
|
391
|
+
ctxWithPluginFlags as unknown as ResolvedEmbeddableConfig,
|
|
392
|
+
"sdk-react",
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
396
|
+
expect.any(String),
|
|
397
|
+
expect.stringContaining('const pluginFlags: Partial<PluginFlags> = {"supportsOnComponentReadyHook":true}'),
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("should inject empty pluginFlags when not present in config", async () => {
|
|
402
|
+
vi.mocked(path.relative).mockReturnValue("../../buildDir/buildName");
|
|
403
|
+
|
|
404
|
+
await injectBundleRender(
|
|
405
|
+
ctxWithFileName as unknown as ResolvedEmbeddableConfig,
|
|
406
|
+
"sdk-react",
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
410
|
+
expect.any(String),
|
|
411
|
+
expect.stringContaining("const pluginFlags: Partial<PluginFlags> = {}"),
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("should not leave {{PLUGIN_FLAGS}} token in output", async () => {
|
|
416
|
+
vi.mocked(path.relative).mockReturnValue("../../buildDir/buildName");
|
|
417
|
+
|
|
418
|
+
await injectBundleRender(
|
|
419
|
+
ctxWithFileName as unknown as ResolvedEmbeddableConfig,
|
|
420
|
+
"sdk-react",
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
424
|
+
expect.any(String),
|
|
425
|
+
expect.not.stringContaining("{{PLUGIN_FLAGS}}"),
|
|
426
|
+
);
|
|
427
|
+
});
|
|
366
428
|
});
|
|
367
429
|
|
|
368
430
|
describe("injectCSS cross-platform paths", () => {
|
package/src/generate.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { Logger } from "@stencil/core/internal";
|
|
|
18
18
|
|
|
19
19
|
const STYLE_IMPORTS_TOKEN = "{{STYLES_IMPORT}}";
|
|
20
20
|
const RENDER_IMPORT_TOKEN = "{{RENDER_IMPORT}}";
|
|
21
|
+
const PLUGIN_FLAGS_TOKEN = "{{PLUGIN_FLAGS}}";
|
|
21
22
|
|
|
22
23
|
// stencil doesn't support dynamic component tag name, so we need to replace it manually
|
|
23
24
|
const COMPONENT_TAG_TOKEN = "replace-this-with-component-name";
|
|
@@ -166,6 +167,8 @@ export async function injectBundleRender(
|
|
|
166
167
|
)
|
|
167
168
|
.replaceAll("\\", "/");
|
|
168
169
|
const importStr = `import render from '${importFilePath}/${ctx[pluginName].outputOptions.fileName}';`;
|
|
170
|
+
const pluginFlags = ctx[pluginName].pluginFlags ?? {};
|
|
171
|
+
const pluginFlagsStr = `const pluginFlags: Partial<PluginFlags> = ${JSON.stringify(pluginFlags)}`;
|
|
169
172
|
|
|
170
173
|
let content = await fs.readFile(
|
|
171
174
|
path.resolve(ctx.core.templatesDir, "component.tsx.template"),
|
|
@@ -178,7 +181,7 @@ export async function injectBundleRender(
|
|
|
178
181
|
|
|
179
182
|
await fs.writeFile(
|
|
180
183
|
path.resolve(ctx.client.componentDir, "component.tsx"),
|
|
181
|
-
content.replace(RENDER_IMPORT_TOKEN, importStr),
|
|
184
|
+
content.replace(RENDER_IMPORT_TOKEN, importStr).replace(PLUGIN_FLAGS_TOKEN, pluginFlagsStr),
|
|
182
185
|
);
|
|
183
186
|
}
|
|
184
187
|
|
|
@@ -199,7 +202,7 @@ async function injectBundleRenderStub(
|
|
|
199
202
|
content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
|
|
200
203
|
await fs.writeFile(
|
|
201
204
|
path.resolve(ctx.client.componentDir, "component.tsx"),
|
|
202
|
-
content.replace(RENDER_IMPORT_TOKEN, stubStr),
|
|
205
|
+
content.replace(RENDER_IMPORT_TOKEN, stubStr).replace(PLUGIN_FLAGS_TOKEN, "const pluginFlags: Partial<PluginFlags> = {}"),
|
|
203
206
|
);
|
|
204
207
|
}
|
|
205
208
|
|
package/src/index.ts
CHANGED
|
@@ -4,4 +4,4 @@ export { default as push } from "./push";
|
|
|
4
4
|
export { default as dev } from "./dev";
|
|
5
5
|
export { default as defineConfig } from "./defineConfig";
|
|
6
6
|
export { default as buildPackage } from "./buildPackage";
|
|
7
|
-
export type { ResolvedEmbeddableConfig } from "./defineConfig";
|
|
7
|
+
export type { ResolvedEmbeddableConfig, PluginFlags } from "./defineConfig";
|
package/src/push.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import push, { buildArchive, EMBEDDABLE_FILES } from "./push";
|
|
1
|
+
import push, { buildArchive, EMBEDDABLE_FILES, sendBuild, sendBuildByApiKey } from "./push";
|
|
2
2
|
import provideConfig from "./provideConfig";
|
|
3
3
|
import { fileFromPath } from "formdata-node/file-from-path";
|
|
4
4
|
import * as path from "path";
|
|
@@ -239,7 +239,6 @@ describe("push", () => {
|
|
|
239
239
|
expect(process.exit).toHaveBeenCalledWith(1);
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
|
|
243
242
|
it("should only include component files when pushModels is false", async () => {
|
|
244
243
|
const localZipMock = {
|
|
245
244
|
addFile: vi.fn(),
|
|
@@ -747,4 +746,52 @@ describe("push", () => {
|
|
|
747
746
|
expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.ts")).toBe(false);
|
|
748
747
|
});
|
|
749
748
|
});
|
|
749
|
+
|
|
750
|
+
describe.each([
|
|
751
|
+
{
|
|
752
|
+
label: "sendBuild",
|
|
753
|
+
endpoint: "**/bundle/:workspaceId/upload",
|
|
754
|
+
invoke: (testConfig: ResolvedEmbeddableConfig) =>
|
|
755
|
+
sendBuild(testConfig, { workspaceId: "test-workspace", token: "test-token" }),
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
label: "sendBuildByApiKey",
|
|
759
|
+
endpoint: "**/bundle/upload",
|
|
760
|
+
invoke: (testConfig: ResolvedEmbeddableConfig) =>
|
|
761
|
+
sendBuildByApiKey(testConfig, { apiKey: "test-api-key", email: "test@example.com" }),
|
|
762
|
+
},
|
|
763
|
+
])("$label", ({ endpoint, invoke }) => {
|
|
764
|
+
async function captureMetadata(pushEmbeddables: boolean) {
|
|
765
|
+
let capturedMetadata: Record<string, any> | undefined;
|
|
766
|
+
|
|
767
|
+
server.use(
|
|
768
|
+
http.post(endpoint, async ({ request }) => {
|
|
769
|
+
const formData = await request.formData();
|
|
770
|
+
const requestBlob = formData.get("request") as Blob;
|
|
771
|
+
capturedMetadata = JSON.parse(await requestBlob.text());
|
|
772
|
+
return HttpResponse.json({ bundleId: "mocked-bundle-id" });
|
|
773
|
+
}),
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
const testConfig = {
|
|
777
|
+
...config,
|
|
778
|
+
pushEmbeddables,
|
|
779
|
+
region: "us",
|
|
780
|
+
starterEmbeddables: {},
|
|
781
|
+
} as unknown as ResolvedEmbeddableConfig;
|
|
782
|
+
|
|
783
|
+
await invoke(testConfig);
|
|
784
|
+
return capturedMetadata;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
it("should include pushEmbeddables in form data metadata when true", async () => {
|
|
788
|
+
const metadata = await captureMetadata(true);
|
|
789
|
+
expect(metadata?.pushEmbeddables).toBe(true);
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
it("should include pushEmbeddables in form data metadata when false", async () => {
|
|
793
|
+
const metadata = await captureMetadata(false);
|
|
794
|
+
expect(metadata?.pushEmbeddables).toBe(false);
|
|
795
|
+
});
|
|
796
|
+
});
|
|
750
797
|
});
|
package/src/push.ts
CHANGED
|
@@ -313,6 +313,7 @@ export async function sendBuildByApiKey(
|
|
|
313
313
|
const form = await createFormData(ctx.client.archiveFile, {
|
|
314
314
|
pushModels: ctx.pushModels,
|
|
315
315
|
pushComponents: ctx.pushComponents,
|
|
316
|
+
pushEmbeddables: ctx.pushEmbeddables,
|
|
316
317
|
starterEmbeddableIds: ctx.starterEmbeddables?.[ctx.region],
|
|
317
318
|
authorEmail: email,
|
|
318
319
|
description: message,
|
|
@@ -347,6 +348,7 @@ export async function sendBuild(
|
|
|
347
348
|
const form = await createFormData(ctx.client.archiveFile, {
|
|
348
349
|
pushModels: ctx.pushModels,
|
|
349
350
|
pushComponents: ctx.pushComponents,
|
|
351
|
+
pushEmbeddables: ctx.pushEmbeddables,
|
|
350
352
|
starterEmbeddableIds: ctx.starterEmbeddables?.[ctx.region],
|
|
351
353
|
authorEmail: "",
|
|
352
354
|
description: message,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Component, Watch, Host, Prop, Event, EventEmitter, h, Listen } from '@stencil/core';
|
|
2
|
+
import type { PluginFlags } from '@embeddable.com/sdk-core';
|
|
2
3
|
{{RENDER_IMPORT}}
|
|
4
|
+
{{PLUGIN_FLAGS}}
|
|
3
5
|
|
|
4
6
|
@Component({
|
|
5
7
|
tag: 'replace-this-with-component-name',
|
|
@@ -40,11 +42,13 @@ export class EmbeddableComponent {
|
|
|
40
42
|
|
|
41
43
|
componentDidLoad() {
|
|
42
44
|
this.handlePops();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (!pluginFlags.supportsOnComponentReadyHook) {
|
|
46
|
+
const props = JSON.parse(this.props);
|
|
47
|
+
this.componentDidLoadEvent.emit({
|
|
45
48
|
componentId: props.componentId,
|
|
46
49
|
componentName: this.componentName
|
|
47
|
-
|
|
50
|
+
})
|
|
51
|
+
}
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
disconnectedCallback() {
|
|
@@ -113,7 +117,12 @@ export class EmbeddableComponent {
|
|
|
113
117
|
this.componentName,
|
|
114
118
|
props,
|
|
115
119
|
JSON.parse(this.clientContext),
|
|
116
|
-
this.theme
|
|
120
|
+
this.theme,
|
|
121
|
+
() =>
|
|
122
|
+
this.componentDidLoadEvent.emit({
|
|
123
|
+
componentId: props.componentId,
|
|
124
|
+
componentName: this.componentName
|
|
125
|
+
})
|
|
117
126
|
);
|
|
118
127
|
}
|
|
119
128
|
}
|