mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-05 03:28:59 +09:00
experimental: Allow json in template literals (#3886)
## Description closes #3872 - [x] - Builder support - [x] - Publish support ## Steps for reproduction Example https://add-json.staging.webstudio.is/builder/8160e16d-5ab4-4249-86de-0c98a64526a3 https://test-json-ki9yj.wstd.work/ ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
This commit is contained in:
1 parent
403d7b2328
commit
13da170ef4
38 files changed
+888
-173
No files matched your search
@@ -0,0 +1,15 @@
|
||||
import { expect, test } from "@jest/globals";
|
||||
import { evaluateExpressionWithinScope } from "./binding-popover";
|
||||
import { encodeDataSourceVariable } from "@webstudio-is/sdk";
|
||||
|
||||
test("evaluateExpressionWithinScope works", () => {
|
||||
const variableName = "jsonVariable";
|
||||
const encVariableName = encodeDataSourceVariable(variableName);
|
||||
const variableValue = 1;
|
||||
|
||||
expect(
|
||||
evaluateExpressionWithinScope(`${encVariableName} + ${encVariableName}`, {
|
||||
[encVariableName]: variableValue,
|
||||
})
|
||||
).toEqual(2);
|
||||
});
|
||||
@@ -46,23 +46,21 @@ import {
|
||||
type EditorApi,
|
||||
} from "./expression-editor";
|
||||
import { useSideOffset } from "./floating-panel";
|
||||
import { $dataSourceVariables } from "~/shared/nano-states";
|
||||
import { $dataSourceVariables, computeExpression } from "~/shared/nano-states";
|
||||
|
||||
export const evaluateExpressionWithinScope = (
|
||||
expression: string,
|
||||
scope: Record<string, unknown>
|
||||
) => {
|
||||
let expressionWithScope = "";
|
||||
const variables = new Map<string, unknown>();
|
||||
for (const [name, value] of Object.entries(scope)) {
|
||||
expressionWithScope += `const ${name} = ${JSON.stringify(value)};\n`;
|
||||
}
|
||||
expressionWithScope += `return (${expression})`;
|
||||
try {
|
||||
const fn = new Function(expressionWithScope);
|
||||
return fn();
|
||||
} catch {
|
||||
//
|
||||
const decodedName = decodeDataSourceVariable(name);
|
||||
if (decodedName) {
|
||||
variables.set(decodedName, value);
|
||||
}
|
||||
}
|
||||
|
||||
return computeExpression(expression, variables);
|
||||
};
|
||||
|
||||
const BindingPanel = ({
|
||||
@@ -447,6 +445,7 @@ export const BindingPopover = ({
|
||||
value,
|
||||
scope
|
||||
);
|
||||
|
||||
onRemove(evaluatedValue);
|
||||
preventedClosing.current = false;
|
||||
hasUnsavedChange.current = false;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { beforeEach, expect, test } from "@jest/globals";
|
||||
import { cleanStores } from "nanostores";
|
||||
import { createDefaultPages } from "@webstudio-is/project-build";
|
||||
import { setEnv } from "@webstudio-is/feature-flags";
|
||||
import type { Instance } from "@webstudio-is/sdk";
|
||||
import { encodeDataSourceVariable, type Instance } from "@webstudio-is/sdk";
|
||||
import {
|
||||
collectionComponent,
|
||||
textContentAttribute,
|
||||
@@ -993,3 +993,20 @@ test("prefill default system variable value", () => {
|
||||
test("compute expression when invalid syntax", () => {
|
||||
expect(computeExpression("https://github.com", new Map())).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("compute literal expression when variable is json object", () => {
|
||||
const variableName = "jsonVariable";
|
||||
const encVariableName = encodeDataSourceVariable(variableName);
|
||||
const jsonObject = { hello: "world", subObject: { world: "hello" } };
|
||||
const variables = new Map([[variableName, jsonObject]]);
|
||||
const expression = "`${" + encVariableName + "}`";
|
||||
|
||||
expect(computeExpression(expression, variables)).toEqual(
|
||||
JSON.stringify(jsonObject)
|
||||
);
|
||||
|
||||
const subObjectExpression = "`${" + encVariableName + ".subObject}`";
|
||||
expect(computeExpression(subObjectExpression, variables)).toEqual(
|
||||
JSON.stringify(jsonObject.subObject)
|
||||
);
|
||||
});
|
||||
@@ -9,8 +9,10 @@ import type {
|
||||
ImageAsset,
|
||||
} from "@webstudio-is/sdk";
|
||||
import {
|
||||
createJsonStringifyProxy,
|
||||
decodeDataSourceVariable,
|
||||
encodeDataSourceVariable,
|
||||
isPlainObject,
|
||||
transpileExpression,
|
||||
} from "@webstudio-is/sdk";
|
||||
import {
|
||||
@@ -223,7 +225,15 @@ export const computeExpression = (
|
||||
code += `let ${identifier} = _variables.get("${id}");\n`;
|
||||
}
|
||||
code += `return (${transpiled})`;
|
||||
const result = new Function("_variables", code)(variables);
|
||||
|
||||
const proxiedVariables = new Map(
|
||||
[...variables.entries()].map(([key, value]) => [
|
||||
key,
|
||||
isPlainObject(value) ? createJsonStringifyProxy(value) : value,
|
||||
])
|
||||
);
|
||||
|
||||
const result = new Function("_variables", code)(proxiedVariables);
|
||||
return result;
|
||||
} catch {
|
||||
// empty block
|
||||
|
||||
Reference in new issue
Block a user