@webstudio-is/react-sdk 0.86.0 → 0.87.0

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.
@@ -1,21 +1,31 @@
1
1
  import type { Instance, Prop } from "@webstudio-is/project-build";
2
+ import type { IndexesWithinAncestors } from "./instance-utils";
2
3
  /**
3
4
  * Hooks are subscriptions to builder events
4
5
  * with limited way to interact with it.
5
6
  * Called independently from components.
6
7
  */
7
8
  export type HookContext = {
9
+ indexesWithinAncestors: IndexesWithinAncestors;
10
+ getPropValue: (instanceId: Instance["id"], propName: Prop["name"]) => unknown;
8
11
  setPropVariable: (instanceId: Instance["id"], propName: Prop["name"], value: unknown) => void;
9
12
  };
10
- export type InstanceSelection = Instance[];
13
+ export type InstancePath = Instance[];
11
14
  type NavigatorEvent = {
12
- instanceSelection: InstanceSelection;
15
+ /**
16
+ * List of instances from selected to the root
17
+ */
18
+ instancePath: InstancePath;
13
19
  };
14
20
  export type Hook = {
15
21
  onNavigatorSelect?: (context: HookContext, event: NavigatorEvent) => void;
16
22
  onNavigatorUnselect?: (context: HookContext, event: NavigatorEvent) => void;
17
23
  };
18
- export declare const getClosestInstance: (instanceSelection: InstanceSelection, currentInstance: Instance, closestComponent: Instance["component"]) => {
24
+ /**
25
+ * Find closest matching instance by component name
26
+ * by lookup in instance path
27
+ */
28
+ export declare const getClosestInstance: (instancePath: InstancePath, currentInstance: Instance, closestComponent: Instance["component"]) => {
19
29
  type: "instance";
20
30
  id: string;
21
31
  component: string;
@@ -0,0 +1 @@
1
+ export {};
@@ -11,3 +11,4 @@ export { validateExpression, generateComputingExpressions, executeComputingExpre
11
11
  export { renderComponentTemplate } from "./component-renderer";
12
12
  export { getIndexesWithinAncestors } from "./instance-utils";
13
13
  export * from "./hook";
14
+ export { generateUtilsExport } from "./generator";
@@ -3,8 +3,8 @@ import { type Build, type Page } from "@webstudio-is/project-build";
3
3
  import type { Asset } from "@webstudio-is/asset-uploader";
4
4
  import { type WebstudioComponentProps } from "./webstudio-component";
5
5
  import type { Components } from "../components/components-utils";
6
- import type { Params, DataSourceValues } from "../context";
7
- import type { IndexesWithinAncestors } from "../instance-utils";
6
+ import type { Params } from "../context";
7
+ import type { GeneratedUtils } from "../generator";
8
8
  export type Data = {
9
9
  page: Page;
10
10
  pages: Array<Page>;
@@ -17,12 +17,10 @@ export type RootPropsData = Omit<Data, "build"> & {
17
17
  };
18
18
  type RootProps = {
19
19
  data: RootPropsData;
20
- indexesWithinAncestors: IndexesWithinAncestors;
21
- executeComputingExpressions: (values: DataSourceValues) => DataSourceValues;
22
- executeEffectfulExpression: (expression: string, args: DataSourceValues, values: DataSourceValues) => DataSourceValues;
20
+ utils: GeneratedUtils;
23
21
  Component?: ForwardRefExoticComponent<WebstudioComponentProps & RefAttributes<HTMLElement>>;
24
22
  components: Components;
25
23
  scripts?: ReactNode;
26
24
  };
27
- export declare const InstanceRoot: ({ data, indexesWithinAncestors, executeComputingExpressions, executeEffectfulExpression, Component, components, scripts, }: RootProps) => JSX.Element | null;
25
+ export declare const InstanceRoot: ({ data, utils, Component, components, scripts, }: RootProps) => JSX.Element | null;
28
26
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/react-sdk",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "description": "Webstudio JavaScript / TypeScript API",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -8,12 +8,12 @@
8
8
  "devDependencies": {
9
9
  "@jest/globals": "^29.6.2",
10
10
  "@remix-run/react": "^1.19.2",
11
- "@types/react": "^18.2.16",
11
+ "@types/react": "^18.2.20",
12
12
  "@types/react-dom": "^18.2.7",
13
13
  "jest": "^29.6.2",
14
14
  "react": "^18.2.0",
15
15
  "react-dom": "^18.2.0",
16
- "type-fest": "^3.7.1",
16
+ "type-fest": "^4.2.0",
17
17
  "typescript": "5.1.6",
18
18
  "zod": "^3.21.4",
19
19
  "@webstudio-is/jest-config": "^1.0.7",
@@ -34,12 +34,12 @@
34
34
  "nanoevents": "^8.0.0",
35
35
  "nanoid": "^4.0.2",
36
36
  "nanostores": "^0.9.3",
37
- "@webstudio-is/asset-uploader": "^0.86.0",
38
- "@webstudio-is/css-data": "^0.86.0",
39
- "@webstudio-is/css-engine": "^0.86.0",
40
- "@webstudio-is/fonts": "^0.86.0",
41
- "@webstudio-is/generate-arg-types": "^0.86.0",
42
- "@webstudio-is/project-build": "^0.86.0"
37
+ "@webstudio-is/asset-uploader": "^0.87.0",
38
+ "@webstudio-is/css-data": "^0.87.0",
39
+ "@webstudio-is/css-engine": "^0.87.0",
40
+ "@webstudio-is/fonts": "^0.87.0",
41
+ "@webstudio-is/generate-arg-types": "^0.87.0",
42
+ "@webstudio-is/project-build": "^0.87.0"
43
43
  },
44
44
  "exports": {
45
45
  ".": {
@@ -2,7 +2,10 @@ import type { ExoticComponent } from "react";
2
2
  import type { Instance } from "@webstudio-is/project-build";
3
3
  import { getStyleDeclKey } from "@webstudio-is/project-build";
4
4
  import type { WsComponentMeta } from "./components/component-meta";
5
- import { generateDataFromEmbedTemplate } from "./embed-template";
5
+ import {
6
+ WsEmbedTemplate,
7
+ generateDataFromEmbedTemplate,
8
+ } from "./embed-template";
6
9
  import { generateCssText } from "./css";
7
10
  import { InstanceRoot, WebstudioComponent } from "./tree";
8
11
  import {
@@ -18,23 +21,43 @@ export const renderComponentTemplate = ({
18
21
  name,
19
22
  metas: metasRecord,
20
23
  components,
24
+ props,
21
25
  }: {
22
26
  name: Instance["component"];
23
27
  metas: Record<string, WsComponentMeta>;
28
+ props?: Record<string, unknown>;
24
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
30
  components: Record<string, ExoticComponent<any>>;
26
31
  }) => {
27
32
  const metas = new Map(Object.entries(metasRecord));
28
- const data = generateDataFromEmbedTemplate(
29
- metas.get(name)?.template ?? [
30
- {
31
- type: "instance",
32
- component: name,
33
- children: [],
34
- },
35
- ],
36
- "base"
37
- );
33
+
34
+ const template: WsEmbedTemplate = metas.get(name)?.template ?? [
35
+ {
36
+ type: "instance",
37
+ component: name,
38
+ children: [],
39
+ },
40
+ ];
41
+
42
+ if (template[0].type === "instance" && props !== undefined) {
43
+ template[0].props = Object.entries(props).map(([prop, value]) => {
44
+ if (typeof value === "string") {
45
+ return { type: "string", name: prop, value: value };
46
+ }
47
+
48
+ if (typeof value === "number") {
49
+ return { type: "number", name: prop, value: value };
50
+ }
51
+
52
+ if (typeof value === "boolean") {
53
+ return { type: "boolean", name: prop, value: value };
54
+ }
55
+ throw new Error(`Unsupported prop ${props} with value ${value}`);
56
+ });
57
+ }
58
+
59
+ const data = generateDataFromEmbedTemplate(template, "base");
60
+
38
61
  const instances: [Instance["id"], Instance][] = [
39
62
  [
40
63
  "root",
@@ -49,6 +72,7 @@ export const renderComponentTemplate = ({
49
72
  (instance) => [instance.id, instance] satisfies [Instance["id"], Instance]
50
73
  ),
51
74
  ];
75
+
52
76
  return (
53
77
  <>
54
78
  <style>
@@ -87,30 +111,35 @@ export const renderComponentTemplate = ({
87
111
  ]),
88
112
  },
89
113
  }}
90
- executeComputingExpressions={(values) => {
91
- const expressions = new Map<string, string>();
92
- for (const dataSource of data.dataSources) {
93
- const name = encodeDataSourceVariable(dataSource.id);
94
- if (dataSource.type === "expression") {
95
- expressions.set(name, dataSource.code);
114
+ utils={{
115
+ indexesWithinAncestors: getIndexesWithinAncestors(
116
+ metas,
117
+ new Map(instances),
118
+ ["root"]
119
+ ),
120
+ executeComputingExpressions: (values) => {
121
+ const expressions = new Map<string, string>();
122
+ for (const dataSource of data.dataSources) {
123
+ const name = encodeDataSourceVariable(dataSource.id);
124
+ if (dataSource.type === "expression") {
125
+ expressions.set(name, dataSource.code);
126
+ }
96
127
  }
97
- }
98
- return decodeVariablesMap(
99
- executeComputingExpressions(expressions, encodeVariablesMap(values))
100
- );
101
- }}
102
- executeEffectfulExpression={(code, args, values) => {
103
- return decodeVariablesMap(
104
- executeEffectfulExpression(code, args, encodeVariablesMap(values))
105
- );
128
+ return decodeVariablesMap(
129
+ executeComputingExpressions(
130
+ expressions,
131
+ encodeVariablesMap(values)
132
+ )
133
+ );
134
+ },
135
+ executeEffectfulExpression: (code, args, values) => {
136
+ return decodeVariablesMap(
137
+ executeEffectfulExpression(code, args, encodeVariablesMap(values))
138
+ );
139
+ },
106
140
  }}
107
141
  Component={WebstudioComponent}
108
142
  components={new Map(Object.entries(components))}
109
- indexesWithinAncestors={getIndexesWithinAncestors(
110
- metas,
111
- new Map(instances),
112
- ["root"]
113
- )}
114
143
  />
115
144
  </>
116
145
  );
@@ -63,7 +63,7 @@ const WsComponentMeta = z.object({
63
63
  // copied or dragged out of its parent instance
64
64
  // true by default
65
65
  detachable: z.optional(z.boolean()),
66
- label: z.string(),
66
+ label: z.optional(z.string()),
67
67
  description: z.string().optional(),
68
68
  icon: z.string(),
69
69
  presetStyle: z.optional(z.record(z.string(), EmbedTemplateStyleDecl)),
@@ -0,0 +1,169 @@
1
+ import { expect, test } from "@jest/globals";
2
+ import { generateUtilsExport } from "./generator";
3
+
4
+ test("generates utils", () => {
5
+ expect(
6
+ generateUtilsExport({
7
+ page: {
8
+ id: "",
9
+ path: "",
10
+ name: "",
11
+ title: "",
12
+ meta: {},
13
+ rootInstanceId: "tabs",
14
+ },
15
+ metas: new Map([
16
+ ["Tabs", { type: "container", label: "", icon: "" }],
17
+ [
18
+ "TabsContent",
19
+ {
20
+ type: "container",
21
+ label: "",
22
+ icon: "",
23
+ indexWithinAncestor: "Tabs",
24
+ },
25
+ ],
26
+ ]),
27
+ instances: new Map([
28
+ [
29
+ "tabs",
30
+ {
31
+ id: "tabs",
32
+ type: "instance",
33
+ component: "Tabs",
34
+ children: [
35
+ { type: "id", value: "content1" },
36
+ { type: "id", value: "content2" },
37
+ ],
38
+ },
39
+ ],
40
+
41
+ [
42
+ "content1",
43
+ {
44
+ id: "content1",
45
+ type: "instance",
46
+ component: "TabsContent",
47
+ children: [],
48
+ },
49
+ ],
50
+
51
+ [
52
+ "content2",
53
+ {
54
+ id: "content2",
55
+ type: "instance",
56
+ component: "TabsContent",
57
+ children: [],
58
+ },
59
+ ],
60
+ ]),
61
+ props: new Map([
62
+ [
63
+ "open",
64
+ {
65
+ type: "dataSource",
66
+ id: "open",
67
+ instanceId: "tabs",
68
+ name: "open",
69
+ value: "tabsOpen",
70
+ },
71
+ ],
72
+
73
+ [
74
+ "onOpenChange",
75
+ {
76
+ type: "action",
77
+ id: "onOpenChange",
78
+ instanceId: "tabs",
79
+ name: "onOpenChange",
80
+ value: [
81
+ {
82
+ type: "execute",
83
+ args: ["open"],
84
+ code: `$ws$dataSource$tabsOpen = open`,
85
+ },
86
+ ],
87
+ },
88
+ ],
89
+ ]),
90
+ dataSources: new Map([
91
+ [
92
+ "tabsOpen",
93
+ {
94
+ id: "tabsOpen",
95
+ name: "tabsOpen",
96
+ scopeInstanceId: "tabs",
97
+ type: "variable",
98
+ value: { type: "string", value: "0" },
99
+ },
100
+ ],
101
+ ]),
102
+ })
103
+ ).toMatchInlineSnapshot(`
104
+ "
105
+ /* eslint-disable */
106
+
107
+ const indexesWithinAncestors = new Map<string, number>([
108
+ ["content1", 0],
109
+ ["content2", 1],
110
+
111
+ ]);
112
+
113
+ const rawExecuteComputingExpressions = (
114
+ _variables: Map<string, unknown>
115
+ ): Map<string, unknown> => {
116
+ return new Map([
117
+ ]);
118
+ };
119
+ const executeComputingExpressions = (variables: Map<string, unknown>) => {
120
+ const encodedvariables = sdk.encodeVariablesMap(variables);
121
+ const encodedResult = rawExecuteComputingExpressions(encodedvariables);
122
+ return sdk.decodeVariablesMap(encodedResult);
123
+ };
124
+
125
+ const generatedEffectfulExpressions = new Map<
126
+ string,
127
+ (args: Map<string, any>, variables: Map<string, any>) => Map<string, unknown>
128
+ >([
129
+ ["$ws$dataSource$tabsOpen = open", (_args: Map<string, any>, _variables: Map<string, any>) => { let open = _args.get('open');
130
+ let $ws$dataSource$tabsOpen;
131
+ $ws$dataSource$tabsOpen = open;
132
+ return new Map([
133
+ ['$ws$dataSource$tabsOpen', $ws$dataSource$tabsOpen],
134
+ ]); })],
135
+
136
+ ]);
137
+
138
+ const rawExecuteEffectfulExpression = (
139
+ code: string,
140
+ args: Map<string, unknown>,
141
+ variables: Map<string, unknown>
142
+ ): Map<string, unknown> => {
143
+ if(generatedEffectfulExpressions.has(code)) {
144
+ return generatedEffectfulExpressions.get(code)!(args, variables);
145
+ }
146
+ console.error("Effectful expression not found", code);
147
+ throw new Error("Effectful expression not found");
148
+ };
149
+
150
+ const executeEffectfulExpression = (
151
+ code: string,
152
+ args: Map<string, unknown>,
153
+ variables: Map<string, unknown>
154
+ ) => {
155
+ const encodedvariables = sdk.encodeVariablesMap(variables);
156
+ const encodedResult = rawExecuteEffectfulExpression(code, args, encodedvariables);
157
+ return sdk.decodeVariablesMap(encodedResult);
158
+ };
159
+
160
+ export const utils = {
161
+ indexesWithinAncestors,
162
+ executeComputingExpressions,
163
+ executeEffectfulExpression,
164
+ };
165
+
166
+ /* eslint-enable */
167
+ "
168
+ `);
169
+ });
@@ -0,0 +1,147 @@
1
+ import type {
2
+ DataSources,
3
+ Instance,
4
+ Instances,
5
+ Page,
6
+ Props,
7
+ } from "@webstudio-is/project-build";
8
+ import type { WsComponentMeta } from "./components/component-meta";
9
+ import {
10
+ getIndexesWithinAncestors,
11
+ type IndexesWithinAncestors,
12
+ } from "./instance-utils";
13
+ import {
14
+ encodeDataSourceVariable,
15
+ generateComputingExpressions,
16
+ generateEffectfulExpression,
17
+ } from "./expression";
18
+ import type { DataSourceValues } from "./context";
19
+
20
+ type PageData = {
21
+ page: Page;
22
+ metas: Map<Instance["component"], WsComponentMeta>;
23
+ instances: Instances;
24
+ props: Props;
25
+ dataSources: DataSources;
26
+ };
27
+
28
+ export type GeneratedUtils = {
29
+ indexesWithinAncestors: IndexesWithinAncestors;
30
+ executeComputingExpressions: (values: DataSourceValues) => DataSourceValues;
31
+ executeEffectfulExpression: (
32
+ expression: string,
33
+ args: DataSourceValues,
34
+ values: DataSourceValues
35
+ ) => DataSourceValues;
36
+ };
37
+
38
+ /**
39
+ * Generates data based utilities at build time
40
+ * Requires this import statement in scope
41
+ * import * as sdk from "@webstudio-is/react-sdk";
42
+ */
43
+ export const generateUtilsExport = (siteData: PageData) => {
44
+ const indexesWithinAncestors = getIndexesWithinAncestors(
45
+ siteData.metas,
46
+ siteData.instances,
47
+ [siteData.page.rootInstanceId]
48
+ );
49
+ let indexesWithinAncestorsEntries = "";
50
+ for (const [key, value] of indexesWithinAncestors) {
51
+ const keyString = JSON.stringify(key);
52
+ const valueString = JSON.stringify(value);
53
+ indexesWithinAncestorsEntries += `[${keyString}, ${valueString}],\n`;
54
+ }
55
+ const generatedIndexesWithinAncestors = `
56
+ const indexesWithinAncestors = new Map<string, number>([
57
+ ${indexesWithinAncestorsEntries}
58
+ ]);
59
+ `;
60
+
61
+ const variables = new Set<string>();
62
+ const expressions = new Map<string, string>();
63
+ for (const dataSource of siteData.dataSources.values()) {
64
+ if (dataSource.type === "variable") {
65
+ variables.add(encodeDataSourceVariable(dataSource.id));
66
+ }
67
+ if (dataSource.type === "expression") {
68
+ expressions.set(encodeDataSourceVariable(dataSource.id), dataSource.code);
69
+ }
70
+ }
71
+ const generatedExecuteComputingExpressions = `
72
+ const rawExecuteComputingExpressions = (
73
+ _variables: Map<string, unknown>
74
+ ): Map<string, unknown> => {
75
+ ${generateComputingExpressions(expressions, variables)}
76
+ };
77
+ const executeComputingExpressions = (variables: Map<string, unknown>) => {
78
+ const encodedvariables = sdk.encodeVariablesMap(variables);
79
+ const encodedResult = rawExecuteComputingExpressions(encodedvariables);
80
+ return sdk.decodeVariablesMap(encodedResult);
81
+ };
82
+ `;
83
+
84
+ let effectfulExpressionsEntries = "";
85
+ for (const prop of siteData.props.values()) {
86
+ if (prop.type === "action") {
87
+ for (const executableValue of prop.value) {
88
+ const codeString = JSON.stringify(executableValue.code);
89
+ const generatedCode = generateEffectfulExpression(
90
+ executableValue.code,
91
+ new Set(executableValue.args),
92
+ variables
93
+ );
94
+ const generatedFunction = `(_args: Map<string, any>, _variables: Map<string, any>) => { ${generatedCode} })`;
95
+
96
+ effectfulExpressionsEntries += `[${codeString}, ${generatedFunction}],\n`;
97
+ }
98
+ }
99
+ }
100
+ const generatedExecuteEffectfulExpression = `const generatedEffectfulExpressions = new Map<
101
+ string,
102
+ (args: Map<string, any>, variables: Map<string, any>) => Map<string, unknown>
103
+ >([
104
+ ${effectfulExpressionsEntries}
105
+ ]);
106
+
107
+ const rawExecuteEffectfulExpression = (
108
+ code: string,
109
+ args: Map<string, unknown>,
110
+ variables: Map<string, unknown>
111
+ ): Map<string, unknown> => {
112
+ if(generatedEffectfulExpressions.has(code)) {
113
+ return generatedEffectfulExpressions.get(code)!(args, variables);
114
+ }
115
+ console.error("Effectful expression not found", code);
116
+ throw new Error("Effectful expression not found");
117
+ };
118
+
119
+ const executeEffectfulExpression = (
120
+ code: string,
121
+ args: Map<string, unknown>,
122
+ variables: Map<string, unknown>
123
+ ) => {
124
+ const encodedvariables = sdk.encodeVariablesMap(variables);
125
+ const encodedResult = rawExecuteEffectfulExpression(code, args, encodedvariables);
126
+ return sdk.decodeVariablesMap(encodedResult);
127
+ };
128
+ `;
129
+
130
+ return `
131
+ /* eslint-disable */
132
+
133
+ ${generatedIndexesWithinAncestors.trim()}
134
+
135
+ ${generatedExecuteComputingExpressions.trim()}
136
+
137
+ ${generatedExecuteEffectfulExpression.trim()}
138
+
139
+ export const utils = {
140
+ indexesWithinAncestors,
141
+ executeComputingExpressions,
142
+ executeEffectfulExpression,
143
+ };
144
+
145
+ /* eslint-enable */
146
+ `;
147
+ };
@@ -0,0 +1,15 @@
1
+ import { expect, test } from "@jest/globals";
2
+ import { getClosestInstance, type InstancePath } from ".";
3
+
4
+ test("get closest instance", () => {
5
+ const instancePath: InstancePath = [
6
+ { type: "instance", id: "4", component: "Content", children: [] },
7
+ { type: "instance", id: "3", component: "Tabs", children: [] },
8
+ { type: "instance", id: "2", component: "Content", children: [] },
9
+ { type: "instance", id: "1", component: "Tabs", children: [] },
10
+ { type: "instance", id: "0", component: "Body", children: [] },
11
+ ];
12
+ const [content2, tabs2, content1, tabs1, _body] = instancePath;
13
+ expect(getClosestInstance(instancePath, content2, "Tabs")).toBe(tabs2);
14
+ expect(getClosestInstance(instancePath, content1, "Tabs")).toBe(tabs1);
15
+ });
package/src/hook.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Instance, Prop } from "@webstudio-is/project-build";
2
+ import type { IndexesWithinAncestors } from "./instance-utils";
2
3
 
3
4
  /**
4
5
  * Hooks are subscriptions to builder events
@@ -7,6 +8,8 @@ import type { Instance, Prop } from "@webstudio-is/project-build";
7
8
  */
8
9
 
9
10
  export type HookContext = {
11
+ indexesWithinAncestors: IndexesWithinAncestors;
12
+ getPropValue: (instanceId: Instance["id"], propName: Prop["name"]) => unknown;
10
13
  setPropVariable: (
11
14
  instanceId: Instance["id"],
12
15
  propName: Prop["name"],
@@ -14,10 +17,13 @@ export type HookContext = {
14
17
  ) => void;
15
18
  };
16
19
 
17
- export type InstanceSelection = Instance[];
20
+ export type InstancePath = Instance[];
18
21
 
19
22
  type NavigatorEvent = {
20
- instanceSelection: InstanceSelection;
23
+ /**
24
+ * List of instances from selected to the root
25
+ */
26
+ instancePath: InstancePath;
21
27
  };
22
28
 
23
29
  export type Hook = {
@@ -25,13 +31,17 @@ export type Hook = {
25
31
  onNavigatorUnselect?: (context: HookContext, event: NavigatorEvent) => void;
26
32
  };
27
33
 
34
+ /**
35
+ * Find closest matching instance by component name
36
+ * by lookup in instance path
37
+ */
28
38
  export const getClosestInstance = (
29
- instanceSelection: InstanceSelection,
39
+ instancePath: InstancePath,
30
40
  currentInstance: Instance,
31
41
  closestComponent: Instance["component"]
32
42
  ) => {
33
43
  let matched = false;
34
- for (const instance of instanceSelection) {
44
+ for (const instance of instancePath) {
35
45
  if (currentInstance === instance) {
36
46
  matched = true;
37
47
  }
package/src/index.ts CHANGED
@@ -36,3 +36,4 @@ export {
36
36
  export { renderComponentTemplate } from "./component-renderer";
37
37
  export { getIndexesWithinAncestors } from "./instance-utils";
38
38
  export * from "./hook";
39
+ export { generateUtilsExport } from "./generator";
package/src/tree/root.ts CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import { getPropsByInstanceId } from "../props";
22
22
  import type { Components } from "../components/components-utils";
23
23
  import type { Params, DataSourceValues } from "../context";
24
- import type { IndexesWithinAncestors } from "../instance-utils";
24
+ import type { GeneratedUtils } from "../generator";
25
25
 
26
26
  export type Data = {
27
27
  page: Page;
@@ -37,13 +37,7 @@ export type RootPropsData = Omit<Data, "build"> & {
37
37
 
38
38
  type RootProps = {
39
39
  data: RootPropsData;
40
- indexesWithinAncestors: IndexesWithinAncestors;
41
- executeComputingExpressions: (values: DataSourceValues) => DataSourceValues;
42
- executeEffectfulExpression: (
43
- expression: string,
44
- args: DataSourceValues,
45
- values: DataSourceValues
46
- ) => DataSourceValues;
40
+ utils: GeneratedUtils;
47
41
  Component?: ForwardRefExoticComponent<
48
42
  WebstudioComponentProps & RefAttributes<HTMLElement>
49
43
  >;
@@ -53,13 +47,16 @@ type RootProps = {
53
47
 
54
48
  export const InstanceRoot = ({
55
49
  data,
56
- indexesWithinAncestors,
57
- executeComputingExpressions,
58
- executeEffectfulExpression,
50
+ utils,
59
51
  Component,
60
52
  components,
61
53
  scripts,
62
54
  }: RootProps): JSX.Element | null => {
55
+ const {
56
+ indexesWithinAncestors,
57
+ executeComputingExpressions,
58
+ executeEffectfulExpression,
59
+ } = utils;
63
60
  const dataSourceVariablesStoreRef = useRef<
64
61
  undefined | WritableAtom<DataSourceValues>
65
62
  >(undefined);