Skip to content

Commit

Permalink
feat: switch to new compute node helper
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Aug 7, 2023
1 parent d45962d commit b2e70bc
Show file tree
Hide file tree
Showing 63 changed files with 529 additions and 213 deletions.
8 changes: 8 additions & 0 deletions .changeset/light-snakes-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@marko/translator-default": minor
"@marko/babel-utils": minor
"@marko/compiler": minor
"marko": minor
---

Add compute node helper to replace babels `evaluate` helper. This helper is less aggressive and doesn't suffer from the false positives that popped up with babels version.
6 changes: 6 additions & 0 deletions .changeset/nice-comics-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@marko/translator-default": patch
"marko": patch
---

Avoid adding trailing semicolon to style attribute output.
12 changes: 12 additions & 0 deletions packages/babel-utils/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,15 @@ interface SelectFix {
}[];
initialValue?: string;
}

type Computed =
| undefined
| number
| string
| boolean
| RegExp
| bigint
| null
| { [x: string]: Computed }
| Computed[];
export function computeNode(node: t.Node): undefined | { value: Computed };
194 changes: 194 additions & 0 deletions packages/babel-utils/src/compute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* @param {import("@babel/types").Node} node
*/
export function computeNode(node) {
switch (node.type) {
case "StringLiteral":
case "NumericLiteral":
case "BooleanLiteral":
return { value: node.value };
case "RegExpLiteral":
return { value: new RegExp(node.pattern, node.flags) };
case "NullLiteral":
return { value: null };
case "Identifier":
switch (node.name) {
case "undefined":
return { value: undefined };
case "NaN":
return { value: NaN };
case "Infinity":
return { value: Infinity };
default:
return;
}
case "BigIntLiteral":
return { value: BigInt(node.value) };
case "BinaryExpression": {
const left = computeNode(node.left);
if (!left) return;
const right = computeNode(node.right);
if (!right) return;
switch (node.operator) {
case "+":
return { value: left.value + right.value };
case "-":
return { value: left.value - right.value };
case "*":
return { value: left.value * right.value };
case "/":
return { value: left.value / right.value };
case "%":
return { value: left.value % right.value };
case "**":
return { value: left.value ** right.value };
case "|":
return { value: left.value | right.value };
case "&":
return { value: left.value & right.value };
case "^":
return { value: left.value ^ right.value };
case "<<":
return { value: left.value << right.value };
case ">>":
return { value: left.value >> right.value };
case ">>>":
return { value: left.value >>> right.value };
case "==":
return { value: left.value == right.value };
case "!=":
return { value: left.value != right.value };
case "===":
return { value: left.value === right.value };
case "!==":
return { value: left.value !== right.value };
case "<":
return { value: left.value < right.value };
case "<=":
return { value: left.value <= right.value };
case ">":
return { value: left.value > right.value };
case ">=":
return { value: left.value >= right.value };
default:
return;
}
}
case "UnaryExpression": {
const arg = computeNode(node.argument);
if (!arg) return;
switch (node.operator) {
case "+":
return { value: +arg.value };
case "-":
return { value: -arg.value };
case "~":
return { value: ~arg.value };
case "!":
return { value: !arg.value };
case "typeof":
return { value: typeof arg.value };
case "void":
return { value: void arg.value };
default:
return;
}
}
case "LogicalExpression": {
const left = computeNode(node.left);
if (!left) return;
const right = computeNode(node.right);
if (!right) return;
switch (node.operator) {
case "&&":
return { value: left.value && right.value };
case "||":
return { value: left.value || right.value };
case "??":
return { value: left.value ?? right.value };
default:
return;
}
}
case "ConditionalExpression": {
const test = computeNode(node.test);
if (!test) return;
const consequent = computeNode(node.consequent);
if (!consequent) return;
const alternate = computeNode(node.alternate);
if (!alternate) return;
return { value: test.value ? consequent.value : alternate.value };
}
case "TemplateLiteral": {
let value = node.quasis[0].cooked;
for (let i = 0; i < node.expressions.length; i++) {
const expr = computeNode(node.expressions[i]);
if (!expr) return;
value += expr.value + node.quasis[i + 1].cooked;
}
return { value };
}
case "ObjectExpression": {
const value = {};
for (const prop of node.properties) {
if (prop.decorators) return;
switch (prop.type) {
case "ObjectProperty": {
let key;
if (prop.computed) {
const keyNode = computeNode(prop.key);
if (!keyNode) return;
key = keyNode.value + "";
} else {
switch (prop.key.type) {
case "Identifier":
key = prop.key.name;
break;
case "StringLiteral":
key = prop.key.value;
break;
default:
return;
}
}

const propValue = computeNode(prop.value);
if (!propValue) return;
value[key] = propValue.value;
break;
}
case "SpreadElement": {
const arg = computeNode(prop.argument);
if (!arg) return;
Object.assign(value, arg.value);
break;
}
}
}

return { value };
}
case "ArrayExpression": {
const value = [];
for (const elem of node.elements) {
if (elem) {
if (elem.type === "SpreadElement") {
const arg = computeNode(elem.argument);
if (typeof arg?.value?.[Symbol.iterator] !== "function") return;
for (const item of arg.value) {
value.push(item);
}
} else {
const elemValue = computeNode(elem);
if (!elemValue) return;
value.push(elemValue.value);
}
} else {
value.length++;
}
}

return { value };
}
}
}
1 change: 1 addition & 0 deletions packages/babel-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export {
assertNoVar,
assertNoAttributeTags,
} from "./assert";
export { computeNode } from "./compute";
export { normalizeTemplateString } from "./template-string";

export { getLoc, getLocRange, withLoc } from "./loc";
Expand Down
9 changes: 7 additions & 2 deletions packages/marko/src/runtime/helpers/style-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ module.exports = function styleHelper(style) {

if (type !== "string") {
var styles = "";
var sep = "";

if (Array.isArray(style)) {
for (var i = 0, len = style.length; i < len; i++) {
var next = styleHelper(style[i]);
if (next) styles += next + (next[next.length - 1] !== ";" ? ";" : "");
if (next) {
styles += sep + next;
sep = ";";
}
}
} else if (type === "object") {
for (var name in style) {
Expand All @@ -28,7 +32,8 @@ module.exports = function styleHelper(style) {
value += "px";
}

styles += changeCase.___camelToDashCase(name) + ":" + value + ";";
styles += sep + changeCase.___camelToDashCase(name) + ":" + value;
sep = ";";
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ module.exports = function (helpers) {
color: "red",
});

expect(component.el.getAttribute("style")).to.equal("color:red;");
expect(component.el.getAttribute("style")).to.equal("color:red");
expect(component.getComponent("counter").el.getAttribute("style")).to.equal(
"color:red;"
"color:red"
);
expect(
component.getComponent("counter").el.querySelector(".count").innerHTML
Expand All @@ -16,9 +16,9 @@ module.exports = function (helpers) {
component.getComponent("counter").increment();
component.getComponent("counter").update();

expect(component.el.getAttribute("style")).to.equal("color:red;");
expect(component.el.getAttribute("style")).to.equal("color:red");
expect(component.getComponent("counter").el.getAttribute("style")).to.equal(
"color:red;"
"color:red"
);
expect(
component.getComponent("counter").el.querySelector(".count").innerHTML
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ module.exports = function (helpers) {
});

expect(component.getEl("current").getAttribute("style")).to.equal(
"color:#09c;"
"color:#09c"
);

component.increment();
component.update();

expect(component.getEl("current").getAttribute("style")).to.equal(
"color:#09c;"
"color:#09c"
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ it("should serialize component input down to the browser", function () {
expect(window.barComponent.getComponent("foo")).to.equal(window.fooComponent);
expect(window.fooComponent.input.color).to.equal("#800");
expect(window.fooComponent.el.textContent).to.equal("The current count is 0");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
window.fooComponent.increment();
window.fooComponent.update();
expect(window.fooComponent.el.textContent).to.equal("The current count is 1");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
expect(window.barComponent.getComponent("foo")).to.equal(window.fooComponent);
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ var expect = require("chai").expect;
it("should serialize component input down to the browser", function () {
expect(window.fooComponent.input.color).to.equal("#800");
expect(window.fooComponent.el.textContent).to.equal("The current count is 0");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
window.fooComponent.increment();
window.fooComponent.update();
expect(window.fooComponent.el.textContent).to.equal("The current count is 1");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ it("should serialize component input down to the browser", function () {

expect(window.fooComponent.input.color).to.equal("#800");
expect(window.fooComponent.el.textContent).to.equal("The current count is 0");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
window.fooComponent.increment();
window.fooComponent.update();
expect(window.fooComponent.el.textContent).to.equal("The current count is 1");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800;");
expect(window.fooComponent.el.getAttribute("style")).to.equal("color:#800");
});
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<svg data-dash="case" viewBox="0 0 0 100" class="a" style="color:green;"></svg>
<svg data-dash=case viewBox="0 0 0 100" class=a style=color:green />
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<svg:svg class="a" data-dash="case" style="color:green;" viewBox="0 0 0 100">
<svg:svg class="a" data-dash="case" style="color:green" viewBox="0 0 0 100">
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<hello-world class="foo bar" style="color:blue;">My nested content</hello-world>
<hello-world class="foo bar" style=color:blue>My nested content</hello-world>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<HELLO-WORLD class="foo bar" style="color:blue;">
<HELLO-WORLD class="foo bar" style="color:blue">
"My nested content"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div style="color: red;" id="foo-Frank">Hello Frank!</div>
<div style="color: red" id="foo-Frank">Hello Frank!</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<DIV id="foo-Frank" style="color: red;">
<DIV id="foo-Frank" style="color: red">
"Hello Frank!"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div style="color: red;" id="foo">Hello Frank!</div>
<div style="color: red" id="foo">Hello Frank!</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<DIV id="foo" style="color: red;">
<DIV id="foo" style="color: red">
"Hello Frank!"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div style="color: red;" class="foo">Hello Frank!</div>
<div style="color: red" class="foo">Hello Frank!</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<DIV class="foo" style="color: red;">
<DIV class="foo" style="color: red">
"Hello Frank!"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="foo bar" style="color:blue;">Hello spread</div>
<div class="foo bar" style="color:blue">Hello spread</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<DIV class="foo bar" style="color:blue;">
<DIV class="foo bar" style="color:blue">
"Hello spread"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div style="color:red;font-weight:bold;background-color:blue;"></div>
<div style="color:red;font-weight:bold;background-color:blue"></div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<DIV style="color:red;font-weight:bold;background-color:blue;">
<DIV style="color:red;font-weight:bold;background-color:blue">
Loading

0 comments on commit b2e70bc

Please sign in to comment.